diff --git a/.circleci/config.yml b/.circleci/config.yml index d30a6bb2d4..09e0a4b9b5 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -131,11 +131,9 @@ workflows: branches: ignore: gh-pages - launchpad_such_wow # TODO: Remove this filter once the branch is merged into development - run-ffi-integration-tests: filters: branches: ignore: gh-pages - launchpad_such_wow # TODO: Remove this filter once the branch is merged into development diff --git a/.dockerignore b/.dockerignore index a4a771cb62..3d1d7cfbad 100644 --- a/.dockerignore +++ b/.dockerignore @@ -40,7 +40,3 @@ base_layer/wallet_ffi/build.config base_layer/wallet_ffi/.cargo/config keys.json - -# Ignore docker files -applications/launchpad/docker_rig/*.Dockerfile -applications/launchpad/docker_rig/docker-compose.yml diff --git a/.github/workflows/audit.yml b/.github/workflows/audit.yml index cfdec244d6..2fa01bb612 100644 --- a/.github/workflows/audit.yml +++ b/.github/workflows/audit.yml @@ -1,5 +1,6 @@ --- # Runs daily +--- name: Security audit on: diff --git a/.github/workflows/base_node_binaries.yml b/.github/workflows/base_node_binaries.yml index ce86d6272b..a4ceec9d22 100644 --- a/.github/workflows/base_node_binaries.yml +++ b/.github/workflows/base_node_binaries.yml @@ -113,7 +113,7 @@ jobs: run: | echo "SHARUN=shasum --algorithm 256" >> $GITHUB_ENV echo "CC=gcc" >> $GITHUB_ENV - echo "TBN_EXT=" >> $GITHUB_ENV + echo "TBN_EXT=" >> $GITHUB_ENV echo "LIB_PRE=lib" >> $GITHUB_ENV echo "SHELL_EXT=.sh" >> $GITHUB_ENV echo "PLATFORM_SPECIFIC_DIR=linux" >> $GITHUB_ENV @@ -149,12 +149,6 @@ jobs: - name: Cache cargo files and outputs uses: Swatinem/rust-cache@v1 - - name: Compile launchpad GUI - run: | - cd applications/launchpad/gui-vue - npm install - npm run build - - name: Compile collectibles GUI run: | cd applications/tari_collectibles/web-app @@ -200,7 +194,6 @@ jobs: cp -v "$GITHUB_WORKSPACE/target/release/tari_miner${TBN_EXT}" . cp -v "$GITHUB_WORKSPACE/target/release/tari_validator_node${TBN_EXT}" . cp -v "$GITHUB_WORKSPACE/target/release/tari_collectibles${TBN_EXT}" . - cp -v "$GITHUB_WORKSPACE/target/release/tari_launchpad${TBN_EXT}" . cp -v "$GITHUB_WORKSPACE/target/release/${LIB_PRE}tari_mining_helper_ffi${LIB_EXT}" . cp -v "$GITHUB_WORKSPACE/applications/tari_base_node/${PLATFORM_SPECIFIC_DIR}/runtime/start_tor${SHELL_EXT}" . @@ -236,7 +229,6 @@ jobs: "tari_merge_mining_proxy" "tari_validator_node" "tari_collectibles" - "tari_launchpad" ) for FILE in "${FILES[@]}"; do codesign --options runtime --force --verify --verbose --timestamp --sign "Developer ID Application: $MACOS_APPLICATION_ID" "/tmp/tari_testnet/runtime/$FILE" diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index c7496d5d7a..12c8a16719 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -9,8 +9,6 @@ on: - main - ci-* pull_request: - branches-ignore: - - 'launchpad_such_wow' # TODO: Remove this filter once the branch is merged into development types: - opened - reopened @@ -150,15 +148,7 @@ jobs: runs-on: [ ubuntu-20.04 ] steps: - name: checkout - uses: actions/checkout@v3 - - uses: actions/setup-node@v3 - with: - node-version: 16 - - name: build launchpad gui-vue - run: | - cd applications/launchpad/gui-vue - npm ci - npm run build + uses: actions/checkout@v2 - name: build collectibles web-app run: | cd applications/tari_collectibles/web-app diff --git a/.github/workflows/coverage.yml b/.github/workflows/coverage.yml index 8c1d5e83fb..94d6d9d042 100644 --- a/.github/workflows/coverage.yml +++ b/.github/workflows/coverage.yml @@ -8,6 +8,9 @@ on: - development - ci-coverage-* +env: + toolchain: nightly-2022-05-01 + jobs: coverage: name: test and generate cov diff --git a/.github/workflows/development-ci.yml b/.github/workflows/development-ci.yml index ca890dda87..cf73528c50 100644 --- a/.github/workflows/development-ci.yml +++ b/.github/workflows/development-ci.yml @@ -8,7 +8,7 @@ on: - main env: - toolchain: nightly-022-05-01 + toolchain: nightly-2022-05-01 CARGO_HTTP_MULTIPLEXING: false CARGO_TERM_COLOR: always PROTOC: protoc @@ -20,10 +20,6 @@ jobs: steps: - name: checkout uses: actions/checkout@v2 - - name: npm audit launchpad gui - run: | - cd applications/launchpad/gui-vue - npm audit - name: npm audit collectibles run: | cd applications/tari_collectibles/web-app diff --git a/.github/workflows/launchpad_docker.yml b/.github/workflows/launchpad_docker.yml deleted file mode 100644 index 13c7adbd60..0000000000 --- a/.github/workflows/launchpad_docker.yml +++ /dev/null @@ -1,152 +0,0 @@ ---- -name: Build launchpad docker images - -on: - push: - tags: - - "v[0-9]+.[0-9]+.[0-9]+" - branches: - - build-gha-docker-* - workflow_dispatch: - inputs: - docker_tag: - description: "Docker tag" - required: true - default: "development" - -env: - toolchain: nightly-2022-05-01 - CARGO_HTTP_MULTIPLEXING: false - CARGO_TERM_COLOR: always - -jobs: - docker: - name: build image - strategy: - fail-fast: true - matrix: - image_name: - [ - monerod, - tor, - xmrig, - ] - include: - - image_name: tari_base_node - app_name: base_node - app_exec: tari_base_node - - image_name: tari_wallet - app_name: wallet - app_exec: tari_console_wallet - - image_name: tari_mm_proxy - app_name: mm_proxy - app_exec: tari_merge_mining_proxy - - image_name: tari_sha3_miner - app_name: sha3_miner - app_exec: tari_miner - - runs-on: ubuntu-latest - - steps: - - name: checkout - uses: actions/checkout@v3 - with: - submodules: recursive - - - name: set env - id: environments - run: | - TAG="" - if [ "${{ startsWith(github.ref, 'refs/tags/v') }}" == "true" ] - then - REF=${{github.ref}} - TAG="${REF/refs\/tags\//}" - echo "docker tag from git: $TAG" - else - # Pull App version from file - VAPP=$(awk -F ' = ' \ - '$1 ~ /version/ { gsub(/["]/, "", $2); printf("%s",$2) }' \ - "${GITHUB_WORKSPACE}/applications/tari_base_node/Cargo.toml") - - VBRANCH=$(echo ${GITHUB_REF#refs/heads/}) - VSHA_SHORT=$(git rev-parse --short HEAD) - - TAG="v${VAPP}_${VBRANCH}_$(date -u '+%Y%m%d')_${VSHA_SHORT}" - echo "docker tag from App Version _ git branch _ \ - date stamp _ git short hash: ${TAG}" - fi - - echo "event name: ${{ github.event_name }}" - if [ "${{ github.event_name }}" == "workflow_dispatch" ] - then - TAG="${{ github.event.inputs.docker_tag }}" - echo "docker tag from workflow dispatch: $TAG" - fi - - IMAGE=${{ matrix.image_name }} - echo "image: $IMAGE" - - # Setup dockerfile to use - if [ "${IMAGE:0:5}" == "tari_" ] - then - echo ::set-output name=dockerfile::tarilabs.Dockerfile - # Strip tari_ - IMAGE=${IMAGE/tari_/} - # Strip console_ - IMAGE=${IMAGE/console_/} - echo ::set-output name=app_name::${IMAGE} - echo ::set-output name=dockercontext::./ - else - DOCKERFILE=${IMAGE}.Dockerfile - DOCKERCONTEXT=./applications/launchpad/docker_rig/ - - # Pull the docker image TAG from dockerfile - SUBTAG=$(awk -F '=' '/ARG .*_VERSION=/ \ - { gsub(/["]/, "", $2); printf("%s",$2) }' \ - "${GITHUB_WORKSPACE}/${DOCKERCONTEXT}${DOCKERFILE}") - - echo ::set-output name=dockerfile::${DOCKERFILE} - echo ::set-output name=dockercontext::${DOCKERCONTEXT} - - if [ ! -z "${SUBTAG}" ] - then - TAG="${SUBTAG}_${TAG}" - echo "Adding subtag: ${TAG}" - fi - - fi - - # Set docker image tag - echo "tag: ${TAG}" - echo ::set-output name=tag::$TAG - - - name: Login to Docker Image Provider - uses: docker/login-action@v1 - with: - registry: ${{ secrets.DOCKER_PROVIDER }} - username: ${{ secrets.QUAY_USERNAME }} - password: ${{ secrets.QUAY_ROBOT_TOKEN }} - - - name: Set up QEMU for Docker - uses: docker/setup-qemu-action@v2 - - - name: Set up Docker Buildx - id: buildx - uses: docker/setup-buildx-action@v2 - - - name: Docker image build and push - uses: docker/build-push-action@v3 - with: - context: ${{ steps.environments.outputs.dockercontext }} - file: ./applications/launchpad/docker_rig/${{ steps.environments.outputs.dockerfile }} - platforms: linux/arm64, linux/amd64 - push: true - cache-from: type=gha - cache-to: type=gha,mode=max - build-args: | - VERSION=${{ steps.environments.outputs.tag }} - APP_NAME=${{ matrix.app_name }} - APP_EXEC=${{ matrix.app_exec }} - tags: | - ${{ secrets.DOCKER_PROVIDER }}/${{ secrets.DOCKER_REPO }}/${{ matrix.image_name }}:latest - ${{ secrets.DOCKER_PROVIDER }}/${{ secrets.DOCKER_REPO }}/${{ matrix.image_name }}:${{ steps.environments.outputs.tag }} diff --git a/.github/workflows/libwallet.yml b/.github/workflows/libwallet.yml index 6a5a38f1e9..13aa91a1d7 100644 --- a/.github/workflows/libwallet.yml +++ b/.github/workflows/libwallet.yml @@ -1,5 +1,6 @@ --- # Build a new set of libraries when a new tag containing 'libwallet' is pushed +--- name: Build libwallet on: diff --git a/.github/workflows/long_running.yml b/.github/workflows/long_running.yml index 1a9569ac4a..eafd55797e 100644 --- a/.github/workflows/long_running.yml +++ b/.github/workflows/long_running.yml @@ -1,5 +1,6 @@ --- # Runs weekly (saturday noon) +--- name: Long running integration tests on: diff --git a/.github/workflows/non_critical_integration_tests.yml b/.github/workflows/non_critical_integration_tests.yml index 20a7bda0e4..1f57cf2c44 100644 --- a/.github/workflows/non_critical_integration_tests.yml +++ b/.github/workflows/non_critical_integration_tests.yml @@ -1,5 +1,6 @@ --- # Runs daily (2am) +--- name: Non critical integration tests on: diff --git a/Cargo.toml b/Cargo.toml index 6e3e7eb64b..bc79de03b8 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -23,7 +23,6 @@ members = [ "infrastructure/tari_script", "infrastructure/test_utils", "applications/deps_only", - "applications/launchpad/backend", "applications/tari_base_node", "applications/tari_console_wallet", "applications/tari_collectibles/src-tauri", diff --git a/applications/launchpad/.dockerignore b/applications/launchpad/.dockerignore deleted file mode 100644 index b3f92c7597..0000000000 --- a/applications/launchpad/.dockerignore +++ /dev/null @@ -1,3 +0,0 @@ -build_images.sh -versions.txt -docker_rig/*.Dockerfile \ No newline at end of file diff --git a/applications/launchpad/.gitignore b/applications/launchpad/.gitignore deleted file mode 100644 index b1d86c6ef2..0000000000 --- a/applications/launchpad/.gitignore +++ /dev/null @@ -1,23 +0,0 @@ -# See https://help.github.com/articles/ignoring-files/ for more about ignoring files. - -# dependencies -gui/node_modules -gui/.pnp -gui/.pnp.js - -# testing -gui/coverage - -# production -gui/build - -# misc -.DS_Store -.env.local -.env.development.local -.env.test.local -.env.production.local - -gui/npm-debug.log* -gui/yarn-debug.log* -gui/yarn-error.log* diff --git a/applications/launchpad/.nvmrc b/applications/launchpad/.nvmrc deleted file mode 100644 index 518633e168..0000000000 --- a/applications/launchpad/.nvmrc +++ /dev/null @@ -1 +0,0 @@ -lts/fermium diff --git a/applications/launchpad/backend/.gitignore b/applications/launchpad/backend/.gitignore deleted file mode 100644 index c123704591..0000000000 --- a/applications/launchpad/backend/.gitignore +++ /dev/null @@ -1,4 +0,0 @@ -# Generated by Cargo -# will have compiled files and executables -/target/ -WixTools diff --git a/applications/launchpad/backend/Cargo.toml b/applications/launchpad/backend/Cargo.toml deleted file mode 100644 index dbb30e7c34..0000000000 --- a/applications/launchpad/backend/Cargo.toml +++ /dev/null @@ -1,40 +0,0 @@ -[package] -name = "tari_launchpad" -version = "0.34.0" -description = "The Tari Launcher" -authors = ["The Tari Development Community"] -license = "BSD-3-Clause" -repository = "" -edition = "2018" -build = "src/build.rs" - -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html - -[build-dependencies] -tauri-build = { version = "1.0.0-rc.5", features = [] } - -[dependencies] -tari_app_utilities = { version = "^0.34", path = "../../tari_app_utilities" } -tari_comms = { version = "^0.34", path = "../../../comms/core" } -tari_common = { path="../../../common"} - -bollard = "0.11.1" -config = "0.13.0" -env_logger = "0.9.0" -log = "0.4.14" -rand = "0.8.4" -serde_json = "1.0" -serde = { version = "1.0", features = ["derive"] } -strum = "0.23.0" -strum_macros = "0.23.0" -tauri = { version = "1.0.0-rc.6", features = ["api-all", "cli"] } -tor-hash-passwd = "1.0.1" -thiserror = "1.0.30" -tokio = { version = "1.9", features= ["sync"] } -futures = "0.3" -regex= "1.5.4" -derivative = "2.2.0" - -[features] -default = [ "custom-protocol" ] -custom-protocol = [ "tauri/custom-protocol" ] diff --git a/applications/launchpad/backend/assets/config.toml b/applications/launchpad/backend/assets/config.toml deleted file mode 100644 index 6dc8acd230..0000000000 --- a/applications/launchpad/backend/assets/config.toml +++ /dev/null @@ -1,100 +0,0 @@ -# Config for launchpad v1.0.0 -[base_node] -network = "dibbler" -grpc_address = "/ip4/0.0.0.0/tcp/18142" -override_from = "dibbler" - -[base_node.storage] -track_reorgs = true - -[dibbler.base_node] -identity_file = "/var/tari/base_node/config/dibbler/tari_base_node_id.json" - -[igor.base_node] -network = "igor" -base_node_identity_file = "/var/tari/base_node/config/igor/base_node_id.json" - -[base_node.p2p] -auxiliary_tcp_listener_address = "/dns4/base_node/tcp/18189" - -[base_node.p2p.transport] -type = "tor" - -[base_node.p2p.transport.tor] -control_auth = "password=tari" -socks_address_override = "/dns4/tor/tcp/9050" -control_address = "/dns4/tor/tcp/9051" - -[base_node.p2p.transport.tcp] -listener_address = "/dns4/base_node/tcp/18189" - -[dibbler.p2p.seeds] -dns_seeds = ["seeds.dibbler.tari.com"] -peer_seeds = [ - # 333388d1cbe3e2bd17453d052f - "c2eca9cf32261a1343e21ed718e79f25bfc74386e9305350b06f62047f519347::/onion3/6yxqk2ybo43u73ukfhyc42qn25echn4zegjpod2ccxzr2jd5atipwzqd:18141", - # 555575715a49fc242d756e52ca - "42fcde82b44af1de95a505d858cb31a422c56c4ac4747fbf3da47d648d4fc346::/onion3/2l3e7ysmihc23zybapdrsbcfg6omtjtfkvwj65dstnfxkwtai2fawtyd:18141", - # 77771f53be07fab4be5f1e1ff7 - "50e6aa8f6c50f1b9d9b3d438dfd2a29cfe1f3e3a650bd9e6b1e10f96b6c38f4d::/onion3/7s6y3cz5bnewlj5ypm7sekhgvqjyrq4bpaj5dyvvo7vxydj7hsmyf5ad:18141", - # 9999016f1f3a6162dddf5a45aa - "36a9df45e1423b5315ffa7a91521924210c8e1d1537ad0968450f20f21e5200d::/onion3/v24qfheti2rztlwzgk6v4kdbes3ra7mo3i2fobacqkbfrk656e3uvnid:18141", - # bbbb8358387d81c388fadb4649 - "be128d570e8ec7b15c101ee1a56d6c56dd7d109199f0bd02f182b71142b8675f::/onion3/ha422qsy743ayblgolui5pg226u42wfcklhc5p7nbhiytlsp4ir2syqd:18141", - # eeeeb0a943ed143e613a135392 - "3e0321c0928ca559ab3c0a396272dfaea705efce88440611a38ff3898b097217::/onion3/sl5ledjoaisst6d4fh7kde746dwweuge4m4mf5nkzdhmy57uwgtb7qqd:18141", - # 66664a0f95ce468941bb9de228 - "b0f797e7413b39b6646fa370e8394d3993ead124b8ba24325c3c07a05e980e7e::/ip4/35.177.93.69/tcp/18189", - # 22221bf814d5e524fce9ba5787 - "0eefb45a4de9484eca74846a4f47d2c8d38e76be1fec63b0112bd00d297c0928::/ip4/13.40.98.39/tcp/18189", - # 4444a0efd8388739d563bdd979 - "544ed2baed414307e119d12894e27f9ddbdfa2fd5b6528dc843f27903e951c30::/ip4/13.40.189.176/tcp/18189" -] - -[wallet] -override_from = "dibbler" -db_file = "wallet/wallet.dat" -grpc_address = "/ip4/0.0.0.0/tcp/18143" -password = "tari" -use_libtor = false - -[wallet.p2p] - -[wallet.p2p.transport] -type = "tor" - -[wallet.p2p.transport.tor] -control_auth = "password=tari" -socks_address_override = "/dns4/tor/tcp/9050" -control_address = "/dns4/tor/tcp/9051" - -[wallet.p2p.transport.tcp] -listener_address = "/dns4/wallet/tcp/18188" - -[dibbler.wallet] -network = "dibbler" - -[igor.wallet] -network = "igor" - -[miner] -base_node_addr = "/dns4/base_node/tcp/18142" -wallet_addr = "/dns4/wallet/tcp/18143" -mine_on_tip_only = true -num_mining_threads = 1 - -[merge_mining_proxy] -monerod_url = [ # stagenet - "http://stagenet.xmr-tw.org:38081", - "http://stagenet.community.xmr.to:38081", - "http://monero-stagenet.exan.tech:38081", - "http://xmr-lux.boldsuck.org:38081", - "http://singapore.node.xmr.pm:38081", -] -base_node_grpc_address = "/dns4/base_node/tcp/18142" -console_wallet_grpc_address = "/dns4/wallet/tcp/18143" -listener_address = "/dns4/mm_proxy/tcp/18081" -submit_to_origin = true -monerod_username = "" -monerod_password = "" -monerod_use_auth = false diff --git a/applications/launchpad/backend/assets/log4rs.yml b/applications/launchpad/backend/assets/log4rs.yml deleted file mode 100644 index b81e31783a..0000000000 --- a/applications/launchpad/backend/assets/log4rs.yml +++ /dev/null @@ -1,161 +0,0 @@ -# A customised logfile configuration for running the Tari system in docker - -# timestamp [target] LEVEL message -refresh_rate: 30 seconds -appenders: - # An appender named "stdout" that writes to stdout - stdout: - kind: console - encoder: - pattern: "{d(%Y-%m-%d %H:%M:%S.%f)} [{t}] {h({l}):5} {m}{n}" - - # An appender named "network" that writes to a file with a custom pattern encoder - network: - kind: rolling_file - path: "log/network.log" - policy: - kind: compound - trigger: - kind: size - limit: 10mb - roller: - kind: fixed_window - base: 1 - count: 5 - pattern: "log/network.{}.log" - encoder: - pattern: "{d(%Y-%m-%d %H:%M:%S.%f)} [{t}] {l:5} {m}{n}" - - # An appender named "core" that writes to a file with a custom pattern encoder - core: - kind: rolling_file - path: "log/core.log" - policy: - kind: compound - trigger: - kind: size - limit: 10mb - roller: - kind: fixed_window - base: 1 - count: 5 - pattern: "log/core.{}.log" - encoder: - pattern: "{d(%Y-%m-%d %H:%M:%S.%f)} [{t}] {l:5} {m}{n}" - - # An appender named "other" that writes to a file with a custom pattern encoder - other: - kind: rolling_file - path: "log/other.log" - policy: - kind: compound - trigger: - kind: size - limit: 10mb - roller: - kind: fixed_window - base: 1 - count: 5 - pattern: "log/other.{}.log" - encoder: - pattern: "{d(%Y-%m-%d %H:%M:%S.%f)} [{t}] {l:5} {m}{n}" - -# Set the default logging level to "info" -root: - level: info - appenders: - - core - - stdout - -loggers: - # All events that get logged to core. Many of these are in separate log folders corresponding to their docker containers - tari::application: - level: info - appenders: - - core - additive: false - base_node::app: - level: debug - appenders: - - core - additive: false - c: - level: info - appenders: - - core - additive: false - tari: - level: info - appenders: - - core - additive: false - wallet: - level: info - appenders: - - core - additive: false - tari_miner: - level: debug - appenders: - - core - additive: false - tari_mm_proxy: - level: debug - appenders: - - core - additive: false - - tracing: - level: info - appenders: - - other - additive: false - - # Comms messages get logged to the 'network' log - comms: - level: info - appenders: - - network - additive: false - tari_comms: - level: info - appenders: - - network - additive: false - p2p: - level: info - appenders: - - network - additive: false - yamux: - level: warn - appenders: - - network - additive: false - mio: - level: error - appenders: - - network - additive: false - - # Miscellaneous events. Log these in 'other' - rustyline: - level: error - appenders: - - other - additive: false - tokio_util: - level: error - appenders: - - other - additive: false - pgp: - level: warn - appenders: - - other - additive: false - stress_test: - level: info - appenders: - - other - additive: false diff --git a/applications/launchpad/backend/dist/.gitkeep b/applications/launchpad/backend/dist/.gitkeep deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/applications/launchpad/backend/icons/android-icon-192x192.png b/applications/launchpad/backend/icons/android-icon-192x192.png deleted file mode 100644 index 19a7b783c0..0000000000 Binary files a/applications/launchpad/backend/icons/android-icon-192x192.png and /dev/null differ diff --git a/applications/launchpad/backend/icons/apple-icon-114x114.png b/applications/launchpad/backend/icons/apple-icon-114x114.png deleted file mode 100644 index 5c4d95a0ae..0000000000 Binary files a/applications/launchpad/backend/icons/apple-icon-114x114.png and /dev/null differ diff --git a/applications/launchpad/backend/icons/apple-icon-120x120.png b/applications/launchpad/backend/icons/apple-icon-120x120.png deleted file mode 100644 index 03ee8dabbc..0000000000 Binary files a/applications/launchpad/backend/icons/apple-icon-120x120.png and /dev/null differ diff --git a/applications/launchpad/backend/icons/apple-icon-144x144.png b/applications/launchpad/backend/icons/apple-icon-144x144.png deleted file mode 100644 index 0546994fae..0000000000 Binary files a/applications/launchpad/backend/icons/apple-icon-144x144.png and /dev/null differ diff --git a/applications/launchpad/backend/icons/apple-icon-152x152.png b/applications/launchpad/backend/icons/apple-icon-152x152.png deleted file mode 100644 index 795baaaf1d..0000000000 Binary files a/applications/launchpad/backend/icons/apple-icon-152x152.png and /dev/null differ diff --git a/applications/launchpad/backend/icons/apple-icon-180x180.png b/applications/launchpad/backend/icons/apple-icon-180x180.png deleted file mode 100644 index 874e750cf1..0000000000 Binary files a/applications/launchpad/backend/icons/apple-icon-180x180.png and /dev/null differ diff --git a/applications/launchpad/backend/icons/apple-icon-57x57.png b/applications/launchpad/backend/icons/apple-icon-57x57.png deleted file mode 100644 index 0ce9b1b437..0000000000 Binary files a/applications/launchpad/backend/icons/apple-icon-57x57.png and /dev/null differ diff --git a/applications/launchpad/backend/icons/apple-icon-57x57.png.1 b/applications/launchpad/backend/icons/apple-icon-57x57.png.1 deleted file mode 100644 index 0ce9b1b437..0000000000 Binary files a/applications/launchpad/backend/icons/apple-icon-57x57.png.1 and /dev/null differ diff --git a/applications/launchpad/backend/icons/apple-icon-60x60.png b/applications/launchpad/backend/icons/apple-icon-60x60.png deleted file mode 100644 index 1ff8fcc7ce..0000000000 Binary files a/applications/launchpad/backend/icons/apple-icon-60x60.png and /dev/null differ diff --git a/applications/launchpad/backend/icons/apple-icon-72x72.png b/applications/launchpad/backend/icons/apple-icon-72x72.png deleted file mode 100644 index 86a4c7210d..0000000000 Binary files a/applications/launchpad/backend/icons/apple-icon-72x72.png and /dev/null differ diff --git a/applications/launchpad/backend/icons/apple-icon-76x76.png b/applications/launchpad/backend/icons/apple-icon-76x76.png deleted file mode 100644 index 7b29c60b96..0000000000 Binary files a/applications/launchpad/backend/icons/apple-icon-76x76.png and /dev/null differ diff --git a/applications/launchpad/backend/icons/favicon-16x16.png b/applications/launchpad/backend/icons/favicon-16x16.png deleted file mode 100644 index 223883aacc..0000000000 Binary files a/applications/launchpad/backend/icons/favicon-16x16.png and /dev/null differ diff --git a/applications/launchpad/backend/icons/favicon-32x32.png b/applications/launchpad/backend/icons/favicon-32x32.png deleted file mode 100644 index bbe1e1bb6b..0000000000 Binary files a/applications/launchpad/backend/icons/favicon-32x32.png and /dev/null differ diff --git a/applications/launchpad/backend/icons/favicon-96x96.png b/applications/launchpad/backend/icons/favicon-96x96.png deleted file mode 100644 index 6cbdcfe045..0000000000 Binary files a/applications/launchpad/backend/icons/favicon-96x96.png and /dev/null differ diff --git a/applications/launchpad/backend/icons/icon.ico b/applications/launchpad/backend/icons/icon.ico deleted file mode 100644 index b3636e4b22..0000000000 Binary files a/applications/launchpad/backend/icons/icon.ico and /dev/null differ diff --git a/applications/launchpad/backend/icons/icon.png b/applications/launchpad/backend/icons/icon.png deleted file mode 100644 index 329808c607..0000000000 Binary files a/applications/launchpad/backend/icons/icon.png and /dev/null differ diff --git a/applications/launchpad/backend/icons/tari_issues.png b/applications/launchpad/backend/icons/tari_issues.png deleted file mode 100644 index c559ff3047..0000000000 Binary files a/applications/launchpad/backend/icons/tari_issues.png and /dev/null differ diff --git a/applications/launchpad/backend/package.json b/applications/launchpad/backend/package.json deleted file mode 100644 index 030c00fefd..0000000000 --- a/applications/launchpad/backend/package.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "name": "tari_launchpad", - "version": "1.1.0", - "scripts": { - "tauri": "node ../../tooling/cli.js/bin/tauri" - } -} \ No newline at end of file diff --git a/applications/launchpad/backend/src/build.rs b/applications/launchpad/backend/src/build.rs deleted file mode 100644 index 54943bc094..0000000000 --- a/applications/launchpad/backend/src/build.rs +++ /dev/null @@ -1,6 +0,0 @@ -// Copyright 2022 The Tari Project -// SPDX-License-Identifier: BSD-3-Clause - -fn main() { - tauri_build::build() -} diff --git a/applications/launchpad/backend/src/commands/create_workspace.rs b/applications/launchpad/backend/src/commands/create_workspace.rs deleted file mode 100644 index 015371dcb5..0000000000 --- a/applications/launchpad/backend/src/commands/create_workspace.rs +++ /dev/null @@ -1,81 +0,0 @@ -// Copyright 2021. The Tari Project -// -// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the -// following conditions are met: -// -// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following -// disclaimer. -// -// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the -// following disclaimer in the documentation and/or other materials provided with the distribution. -// -// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote -// products derived from this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, -// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR -// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, -// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE -// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -// - -use std::{ - env, - path::{Path, PathBuf}, - str::FromStr, -}; - -use log::*; -use tauri::{ - api::path::{resolve_path, BaseDirectory}, - AppHandle, - Config, - Manager, - PackageInfo, - Wry, -}; - -use crate::{commands::AppState, docker::create_workspace_folders, error::LauncherError}; - -/// Create a new workspace environment by creating a folder hierarchy (if required) at the `root_folder`, and copying -/// the default config files into it. -#[tauri::command] -pub fn create_new_workspace(app: AppHandle, root_path: Option) -> Result<(), String> { - let config = app.config(); - let package_info = &app.state::().package_info; - let path = root_path - .as_ref() - .map(|r| PathBuf::from_str(r.as_str()).unwrap()) - .unwrap_or_else(|| env::temp_dir().join("tari")); - debug!("Creating workspace at {:?}", path); - create_workspace_folders(&path).map_err(|e| e.chained_message())?; - copy_config_file(path.as_path(), config.as_ref(), package_info, "log4rs.yml").map_err(|e| e.chained_message())?; - copy_config_file(path.as_path(), config.as_ref(), package_info, "config.toml").map_err(|e| e.chained_message())?; - info!("Workspace at {:?} complete!", path); - Ok(()) -} - -pub fn copy_config_file>( - root_path: S, - config: &Config, - package_info: &PackageInfo, - file: &str, -) -> Result<(), LauncherError> { - let path = Path::new("assets").join(file); - let config_path = resolve_path( - config, - package_info, - &Default::default(), - &path, - Some(BaseDirectory::Resource), - )?; - let cfg = std::fs::read_to_string(&config_path).expect("The config assets were not bundled with the App"); - info!("Log Configuration file ({}) loaded", file); - debug!("{}", cfg); - let dest = root_path.as_ref().join("config").join(file); - std::fs::write(&dest, &cfg)?; - info!("Log configuration file ({}) saved to workspace", file); - Ok(()) -} diff --git a/applications/launchpad/backend/src/commands/events.rs b/applications/launchpad/backend/src/commands/events.rs deleted file mode 100644 index 2ab0097251..0000000000 --- a/applications/launchpad/backend/src/commands/events.rs +++ /dev/null @@ -1,65 +0,0 @@ -// Copyright 2021. The Tari Project -// -// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the -// following conditions are met: -// -// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following -// disclaimer. -// -// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the -// following disclaimer in the documentation and/or other materials provided with the distribution. -// -// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote -// products derived from this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, -// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR -// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, -// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE -// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -// - -use futures::StreamExt; -use log::*; -use tauri::{AppHandle, Manager, Wry}; - -use crate::commands::AppState; - -/// Subscribe to system-level docker events, such as container creation, volume, network events etc. -/// This function does not actually return a result, but async commands have a [bug](https://github.com/tauri-apps/tauri/issues/2533) -/// which is avoided by returning a `Result`. -/// -/// A side effect of this command is that events are emitted to the `tari://docker-system-event` channel. Front-ends -/// can listen to this event stream to read in the event messages. -#[tauri::command] -pub async fn events(app: AppHandle) -> Result<(), ()> { - info!("Setting up event stream"); - let state = app.state::(); - let docker = state.docker.read().await; - let mut stream = docker.events().await; - let app_clone = app.clone(); - tauri::async_runtime::spawn(async move { - while let Some(event_result) = stream.next().await { - match event_result { - Ok(event) => { - debug!("Event received: {:?}", event); - if let Err(err) = app_clone.emit_all(event_name(), event) { - warn!("Could not emit event to front-end, {:?}", err); - } - }, - Err(err) => { - warn!("Error in event stream: {:#?}", err) - }, - }; - } - info!("Event stream has closed."); - }); - Ok(()) -} - -/// Extract data from the event object so we know which channel to emit the payload to -pub fn event_name() -> &'static str { - "tari://docker-system-event" -} diff --git a/applications/launchpad/backend/src/commands/launch_docker.rs b/applications/launchpad/backend/src/commands/launch_docker.rs deleted file mode 100644 index 471fcba289..0000000000 --- a/applications/launchpad/backend/src/commands/launch_docker.rs +++ /dev/null @@ -1,198 +0,0 @@ -// Copyright 2021. The Tari Project -// -// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the -// following conditions are met: -// -// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following -// disclaimer. -// -// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the -// following disclaimer in the documentation and/or other materials provided with the distribution. -// -// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote -// products derived from this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, -// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR -// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, -// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE -// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -// - -use std::{convert::TryFrom, path::PathBuf, time::Duration}; - -use derivative::Derivative; -use log::*; -use serde::{Deserialize, Serialize}; -use tauri::{AppHandle, Manager, Wry}; - -use crate::{ - commands::AppState, - docker::{ - helpers::create_password, - BaseNodeConfig, - DockerWrapperError, - LaunchpadConfig, - MmProxyConfig, - Sha3MinerConfig, - TariNetwork, - TariWorkspace, - WalletConfig, - XmRigConfig, - DEFAULT_MINING_ADDRESS, - }, - error::LauncherError, -}; - -#[derive(Derivative, Serialize, Deserialize)] -#[derivative(Debug)] -#[allow(clippy::struct_excessive_bools)] -pub struct WorkspaceLaunchOptions { - root_folder: String, - tari_network: String, - has_base_node: bool, - has_wallet: bool, - has_sha3_miner: bool, - has_mm_proxy: bool, - has_xmrig: bool, - wait_for_tor: Option, - #[derivative(Debug = "ignore")] - #[serde(skip_serializing)] - wallet_password: Option, - sha3_mining_threads: Option, - monerod_url: Option, - monero_username: Option, - #[derivative(Debug = "ignore")] - #[serde(skip_serializing)] - monero_password: Option, - monero_use_auth: Option, - monero_mining_address: Option, - docker_registry: Option, - docker_tag: Option, -} - -impl TryFrom for LaunchpadConfig { - type Error = LauncherError; - - fn try_from(options: WorkspaceLaunchOptions) -> Result { - let tari_network = TariNetwork::try_from(options.tari_network.to_lowercase().as_str())?; - let tor_control_password = create_password(16); - let tor_delay = Duration::from_secs(options.wait_for_tor.unwrap_or(10)); - let base_node = if options.has_base_node { - Some(BaseNodeConfig { delay: tor_delay }) - } else { - None - }; - let wallet = if options.has_wallet { - if options.wallet_password.is_none() { - return Err(LauncherError::ConfigVariableRequired( - "wallet".to_string(), - "wallet_password".to_string(), - )); - } - Some(WalletConfig { - delay: tor_delay, - password: options.wallet_password.unwrap(), - }) - } else { - None - }; - let sha3_miner = if options.has_sha3_miner { - Some(Sha3MinerConfig { - delay: Duration::from_secs(options.wait_for_tor.unwrap_or(15)), - num_mining_threads: options.sha3_mining_threads.unwrap_or(1), - }) - } else { - None - }; - let mm_proxy = if options.has_mm_proxy { - let mut config = MmProxyConfig { - delay: Duration::from_secs(options.wait_for_tor.unwrap_or(15)), - ..Default::default() - }; - if let Some(val) = options.monerod_url { - config.monerod_url = val; - } - if let Some(val) = options.monero_username { - config.monero_username = val; - } - if let Some(val) = options.monero_password { - config.monero_password = val; - } - if let Some(val) = options.monero_use_auth { - config.monero_use_auth = val; - } - Some(config) - } else { - None - }; - let xmrig = if options.has_xmrig { - let monero_mining_address = options - .monero_mining_address - .unwrap_or_else(|| DEFAULT_MINING_ADDRESS.to_string()); - Some(XmRigConfig { - delay: Duration::from_secs(options.wait_for_tor.unwrap_or(20)), - monero_mining_address, - }) - } else { - None - }; - Ok(LaunchpadConfig { - data_directory: PathBuf::from(options.root_folder), - tari_network, - tor_control_password, - base_node, - wallet, - sha3_miner, - mm_proxy, - xmrig, - registry: options.docker_registry, - tag: options.docker_tag, - }) - } -} - -/// This is an example of how we might launch a recipe. It's not currently used in the front-end, but essentially it -/// launches all the containers in a choreographed fashion to stand up the entire mining infra. -#[tauri::command] -pub async fn launch_docker(app: AppHandle, name: String, config: WorkspaceLaunchOptions) -> Result<(), String> { - launch_docker_impl(app, name, config) - .await - .map_err(|e| e.chained_message()) -} - -async fn launch_docker_impl( - app: AppHandle, - name: String, - config: WorkspaceLaunchOptions, -) -> Result<(), LauncherError> { - debug!("Starting docker launch sequence"); - let state = app.state::(); - let docker = state.docker_handle().await; - let should_create_workspace = { - let wrapper = state.workspaces.read().await; - !wrapper.workspace_exists(name.as_str()) - }; // drop read-only lock - { - let mut wrapper = state.workspaces.write().await; - // Now that we have a write lock, we can create a new workspace if required - if should_create_workspace { - let config = LaunchpadConfig::try_from(config)?; - info!( - "Docker workspace does not exist. Creating one with config, {:#?}", - config - ); - wrapper.create_workspace(name.as_str(), config)?; - } - info!("Launching Tari workspace, {}", name); - let workspace: &mut TariWorkspace = wrapper - .get_workspace_mut(name.as_str()) - .ok_or(DockerWrapperError::UnexpectedError)?; - // Pipe docker container logs to Tauri using namespaced events - workspace.start_recipe(docker.clone()).await?; - } // Drop write lock - info!("Tari system, {} has launched", name); - Ok(()) -} diff --git a/applications/launchpad/backend/src/commands/mod.rs b/applications/launchpad/backend/src/commands/mod.rs deleted file mode 100644 index 1810a984a5..0000000000 --- a/applications/launchpad/backend/src/commands/mod.rs +++ /dev/null @@ -1,44 +0,0 @@ -// Copyright 2021. The Tari Project -// -// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the -// following conditions are met: -// -// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following -// disclaimer. -// -// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the -// following disclaimer in the documentation and/or other materials provided with the distribution. -// -// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote -// products derived from this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, -// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR -// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, -// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE -// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -// - -/// ! This module defines all the Tauri commands we expose to the front-end. -/// ! These are generally constructed as wrappers around the lower-level methods in the `docker` module. -/// ! All the commands follow roughly the same pattern: -/// ! - handle input parameters -/// ! - call the the underlying function -/// ! - Map results to JSON and errors to String. -mod create_workspace; -mod events; -mod launch_docker; -mod pull_images; -mod service; -mod shutdown; -mod state; - -pub use create_workspace::create_new_workspace; -pub use events::events; -pub use launch_docker::launch_docker; -pub use pull_images::{image_list, pull_images}; -pub use service::{create_default_workspace, start_service, stop_service}; -pub use shutdown::shutdown; -pub use state::AppState; diff --git a/applications/launchpad/backend/src/commands/pull_images.rs b/applications/launchpad/backend/src/commands/pull_images.rs deleted file mode 100644 index 1be7478d7e..0000000000 --- a/applications/launchpad/backend/src/commands/pull_images.rs +++ /dev/null @@ -1,105 +0,0 @@ -// Copyright 2021. The Tari Project -// -// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the -// following conditions are met: -// -// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following -// disclaimer. -// -// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the -// following disclaimer in the documentation and/or other materials provided with the distribution. -// -// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote -// products derived from this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, -// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR -// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, -// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE -// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -// - -use bollard::models::CreateImageInfo; -use futures::{future::join_all, stream::StreamExt, TryFutureExt}; -use log::{debug, error}; -use serde::Serialize; -use tauri::{AppHandle, Manager, Wry}; - -use crate::{ - commands::AppState, - docker::{ImageType, TariWorkspace}, - error::LauncherError, -}; - -const LOG_TARGET: &str = "tari::launchpad::commands::pull_images"; - -#[derive(Debug, Clone, Serialize)] -pub struct Payload { - image: String, - name: String, - info: CreateImageInfo, -} - -pub static DEFAULT_IMAGES: [ImageType; 8] = [ - ImageType::BaseNode, - ImageType::Wallet, - ImageType::Sha3Miner, - ImageType::Tor, - ImageType::MmProxy, - ImageType::XmRig, - ImageType::Monerod, - ImageType::Frontail, -]; - -/// Provide a list of image names in the Tari "ecosystem" -#[tauri::command] -pub fn image_list() -> Vec { - DEFAULT_IMAGES - .iter() - .map(|&image| image.image_name().to_string()) - .collect() -} - -/// Pulls all the images concurrently using the docker API. -#[tauri::command] -pub async fn pull_images(app: AppHandle) -> Result<(), String> { - debug!("Command pull_images invoked"); - let futures = DEFAULT_IMAGES - .iter() - .map(|image| pull_image(*image, app.clone()).map_err(|e| e.chained_message())); - let results: Vec> = join_all(futures).await; - let errors = results - .into_iter() - .filter(|r| r.is_err()) - .map(|e| e.unwrap_err()) - .collect::>(); - if !errors.is_empty() { - error!("Error pulling images:{}", errors.join("\n")); - return Err(errors.join("\n")); - } - Ok(()) -} - -async fn pull_image(image: ImageType, app: AppHandle) -> Result<(), LauncherError> { - let state = app.state::().clone(); - let docker = state.docker.read().await; - let image_name = TariWorkspace::fully_qualified_image(image, None, None); - let mut stream = docker.pull_image(image_name.clone()).await; - while let Some(update) = stream.next().await { - match update { - Ok(progress) => { - let payload = Payload { - image: image_name.clone(), - name: image.image_name().to_string(), - info: progress, - }; - debug!("Image pull progress:{:?}", payload); - app.emit_all("image-pull-progress", payload)? - }, - Err(err) => return Err(err.into()), - }; - } - Ok(()) -} diff --git a/applications/launchpad/backend/src/commands/service.rs b/applications/launchpad/backend/src/commands/service.rs deleted file mode 100644 index 4cbf3d6a1c..0000000000 --- a/applications/launchpad/backend/src/commands/service.rs +++ /dev/null @@ -1,353 +0,0 @@ -// Copyright 2021. The Tari Project -// -// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the -// following conditions are met: -// -// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following -// disclaimer. -// -// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the -// following disclaimer in the documentation and/or other materials provided with the distribution. -// -// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote -// products derived from this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, -// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR -// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, -// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE -// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -// - -use std::{convert::TryFrom, path::PathBuf, time::Duration}; - -use bollard::Docker; -use derivative::Derivative; -use futures::StreamExt; -use log::*; -use serde::{Deserialize, Serialize}; -use tauri::{AppHandle, Manager, State, Wry}; - -use crate::{ - commands::{create_workspace::copy_config_file, AppState}, - docker::{ - create_workspace_folders, - helpers::create_password, - BaseNodeConfig, - ContainerId, - DockerWrapperError, - ImageType, - LaunchpadConfig, - MmProxyConfig, - Sha3MinerConfig, - TariNetwork, - TariWorkspace, - WalletConfig, - XmRigConfig, - DEFAULT_MINING_ADDRESS, - DEFAULT_MONEROD_URL, - }, - error::LauncherError, -}; - -/// "Global" settings from the launcher front-end -#[derive(Clone, Derivative, Deserialize)] -#[derivative(Debug)] -#[serde(rename_all = "camelCase")] -pub struct ServiceSettings { - pub tari_network: String, - pub root_folder: String, - #[derivative(Debug = "ignore")] - pub wallet_password: String, - pub monero_mining_address: Option, - pub num_mining_threads: i64, - pub docker_registry: Option, - pub docker_tag: Option, - pub monerod_url: Option, - pub monero_username: Option, - #[derivative(Debug = "ignore")] - pub monero_password: Option, - pub monero_use_auth: Option, -} - -impl TryFrom for LaunchpadConfig { - type Error = LauncherError; - - fn try_from(settings: ServiceSettings) -> Result { - let tari_network = TariNetwork::try_from(settings.tari_network.to_lowercase().as_str())?; - let tor_control_password = create_password(16); - // Since most services are launched manually, we set the delay value to zero. - let zero_delay = Duration::from_secs(0); - let base_node = BaseNodeConfig { delay: zero_delay }; - let wallet = WalletConfig { - delay: zero_delay, - password: settings.wallet_password, - }; - let sha3_miner = Sha3MinerConfig { - delay: zero_delay, - num_mining_threads: usize::try_from(settings.num_mining_threads).unwrap(), - }; - let mut mm_proxy = MmProxyConfig { - delay: zero_delay, - monerod_url: settings.monerod_url.unwrap_or_else(|| DEFAULT_MONEROD_URL.to_string()), - ..Default::default() - }; - if let Some(val) = settings.monero_use_auth { - mm_proxy.monero_use_auth = val; - mm_proxy.monero_username = settings.monero_username.unwrap_or_else(|| "".to_string()); - mm_proxy.monero_password = settings.monero_password.unwrap_or_else(|| "".to_string()); - } - let monero_mining_address = settings - .monero_mining_address - .unwrap_or_else(|| DEFAULT_MINING_ADDRESS.to_string()); - let xmrig = XmRigConfig { - delay: Duration::from_secs(15), // Needs to wait for mm_proxy to be ready - monero_mining_address, - }; - Ok(LaunchpadConfig { - data_directory: PathBuf::from(settings.root_folder), - tari_network, - tor_control_password, - base_node: Some(base_node), - wallet: Some(wallet), - sha3_miner: Some(sha3_miner), - mm_proxy: Some(mm_proxy), - xmrig: Some(xmrig), - registry: settings.docker_registry, - tag: settings.docker_tag, - }) - } -} - -/// [`start_service`] returns some useful info that the front-end can use to control the docker environment. -/// In particular, the log and stats event names tell the front end which channels events will be broadcast on. -#[derive(Clone, Debug, Serialize)] -#[serde(rename_all(serialize = "camelCase"))] -pub struct StartServiceResult { - /// The name of the service that was created - name: String, - /// The docker id of the container. Some commands need an id rather than the name. - id: String, - /// What action was taken. Currently not used. - action: String, - /// The name of the event stream to subscribe to for log events. These are the _docker_ logs and not the base node - /// _et. al._ logs. Those are saved to disk and accessible using the usual means, or with frontail. - log_events_name: String, - /// The name of the event stream to subscribe to for resource events (CPU, memory etc). - stats_events_name: String, -} - -/// Starts the specified service -/// -/// The workspace will be created if this is the first call to `start_service`. Otherwise, the settings from the first -/// call will be used and `settings` will be ignored. -/// -/// Starting a service also: -/// - creates a new workspace, if required -/// - creates a network for the network, if required -/// - creates new volumes, if required -/// - creates new node identities, if required -/// - launches the container -/// - creates the log stream -/// - creates the resource stats stream -#[tauri::command] -pub async fn start_service( - app: AppHandle, - service_name: String, - settings: ServiceSettings, -) -> Result { - debug!("start_service called {}", service_name); - start_service_impl(app, service_name, settings).await.map_err(|e| { - let error = e.chained_message(); - error!("{}", error); - error - }) -} - -/// Stops the specified service -/// -/// Stops the container, if it is running. action is " -/// Then, deletes the container. -/// Returns the container id -#[tauri::command] -pub async fn stop_service(state: State<'_, AppState>, service_name: String) -> Result<(), String> { - stop_service_impl(state, service_name).await.map_err(|e| e.to_string()) -} - -/// The "default" workspace is one that is used in the manual front-end configuration (each container is started and -/// stopped manually) -#[tauri::command] -pub async fn create_default_workspace(app: AppHandle, settings: ServiceSettings) -> Result { - create_default_workspace_impl(app, settings).await.map_err(|e| { - let error = e.chained_message(); - error!("{}", error); - error - }) -} - -async fn create_default_workspace_impl(app: AppHandle, settings: ServiceSettings) -> Result { - let config = LaunchpadConfig::try_from(settings)?; - let state = app.state::(); - let app_config = app.config(); - let should_create_workspace = { - let wrapper = state.workspaces.read().await; - !wrapper.workspace_exists("default") - }; // drop read-only lock - if should_create_workspace { - let package_info = &state.package_info; - create_workspace_folders(&config.data_directory)?; - copy_config_file(&config.data_directory, app_config.as_ref(), package_info, "log4rs.yml")?; - copy_config_file(&config.data_directory, app_config.as_ref(), package_info, "config.toml")?; - // Only get a write-lock if we need one - let mut wrapper = state.workspaces.write().await; - wrapper.create_workspace("default", config)?; - } - Ok(should_create_workspace) -} - -async fn start_service_impl( - app: AppHandle, - service_name: String, - settings: ServiceSettings, -) -> Result { - debug!("Starting {} service", service_name); - let state = app.state::(); - let docker = state.docker_handle().await; - let _ = create_default_workspace_impl(app.clone(), settings).await?; - let mut wrapper = state.workspaces.write().await; - // We've just checked this, so it should never fail: - let workspace: &mut TariWorkspace = wrapper - .get_workspace_mut("default") - .ok_or(DockerWrapperError::UnexpectedError)?; - // Check the identity requirements for the service - let ids = workspace.create_or_load_identities()?; - for id in ids.values() { - debug!("Identity loaded: {}", id); - } - // Check network requirements for the service - if !workspace.network_exists(&docker).await? { - workspace.create_network(&docker).await?; - } - // Launch the container - let registry = workspace.config().registry.clone(); - let tag = workspace.config().tag.clone(); - let image = ImageType::try_from(service_name.as_str())?; - let container_name = workspace.start_service(image, registry, tag, docker.clone()).await?; - let state = workspace - .container_mut(container_name.as_str()) - .ok_or(DockerWrapperError::UnexpectedError)?; - let id = state.id().to_string(); - let stats_events_name = stats_event_name(state.id()); - let log_events_name = log_event_name(state.name()); - // Set up event streams - container_logs( - app.clone(), - log_events_name.as_str(), - container_name.as_str(), - &docker, - workspace, - ); - container_stats( - app.clone(), - stats_events_name.as_str(), - container_name.as_str(), - &docker, - workspace, - ); - // Collect data for the return object - let result = StartServiceResult { - name: service_name, - id, - action: "unimplemented".to_string(), - log_events_name, - stats_events_name, - }; - info!("Tari service {} has launched", image.container_name()); - Ok(result) -} - -pub fn log_event_name(container_name: &str) -> String { - format!("tari://docker_log_{}", container_name) -} - -pub fn stats_event_name(container_id: &ContainerId) -> String { - format!("tari://docker_stats_{}", container_id.as_str()) -} - -fn container_logs( - app: AppHandle, - event_name: &str, - container_name: &str, - docker: &Docker, - workspace: &mut TariWorkspace, -) { - info!("Setting up log events for {}", container_name); - if let Some(mut stream) = workspace.logs(container_name, docker) { - let event_name = event_name.to_string(); - tauri::async_runtime::spawn(async move { - while let Some(message) = stream.next().await { - trace!("log event: {:?}", message); - let emit_result = match message { - Ok(payload) => app.emit_all(event_name.as_str(), payload), - Err(err) => app.emit_all(format!("{}_error", event_name).as_str(), err.chained_message()), - }; - if let Err(err) = emit_result { - warn!("Error emitting event: {}", err.to_string()); - } - } - info!("Log stream for {} has closed.", event_name); - }); - info!("Container log events configured."); - } else { - info!( - "Log events could not be configured: {} is not a running container", - container_name - ); - } -} - -fn container_stats( - app: AppHandle, - event_name: &str, - container_name: &str, - docker: &Docker, - workspace: &mut TariWorkspace, -) { - info!("Setting up Resource stats events for {}", container_name); - if let Some(mut stream) = workspace.resource_stats(container_name, docker) { - let event_name = event_name.to_string(); - tauri::async_runtime::spawn(async move { - while let Some(message) = stream.next().await { - trace!("log event: {:?}", message); - let emit_result = match message { - Ok(payload) => app.emit_all(event_name.as_str(), payload), - Err(err) => app.emit_all(format!("{}_error", event_name).as_str(), err.chained_message()), - }; - if let Err(err) = emit_result { - warn!("Error emitting event: {}", err.to_string()); - } - } - info!("Resource stats stream for {} has closed.", event_name); - }); - info!("Resource stats events configured."); - } else { - info!( - "Resource stats events could not be configured: {} is not a running container", - container_name - ); - } -} - -async fn stop_service_impl(state: State<'_, AppState>, service_name: String) -> Result<(), LauncherError> { - let docker = state.docker_handle().await; - let mut wrapper = state.workspaces.write().await; - debug!("Stopping {} service", service_name); - // We've just checked this, so it should never fail: - let workspace: &mut TariWorkspace = wrapper - .get_workspace_mut("default") - .ok_or_else(|| DockerWrapperError::WorkspaceDoesNotExist("default".into()))?; - workspace.stop_container(service_name.as_str(), true, &docker).await; - Ok(()) -} diff --git a/applications/launchpad/backend/src/commands/shutdown.rs b/applications/launchpad/backend/src/commands/shutdown.rs deleted file mode 100644 index d5c1031dd4..0000000000 --- a/applications/launchpad/backend/src/commands/shutdown.rs +++ /dev/null @@ -1,47 +0,0 @@ -// Copyright 2021. The Tari Project -// -// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the -// following conditions are met: -// -// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following -// disclaimer. -// -// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the -// following disclaimer in the documentation and/or other materials provided with the distribution. -// -// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote -// products derived from this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, -// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR -// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, -// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE -// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -// - -use log::*; -use tauri::State; - -use crate::commands::AppState; - -/// Gracefully shutdown all containers and delete them. Blockchain volumes are preserved. -#[tauri::command] -// Return a Result until https://github.com/tauri-apps/tauri/issues/2533 is fixed -pub async fn shutdown(state: State<'_, AppState>) -> Result { - info!("Shutting down"); - let docker = state.docker_handle().await; - let mut workspaces = state.workspaces.write().await; - let msg = match workspaces.shutdown(&docker).await { - Ok(()) => { - info!("Docker has shut down"); - "Docker has shut down" - }, - Err(e) => { - warn!("Docker may not have shut down gracefully. {}", e.chained_message()); - "Docker was not cleanly shut down. See logs for details" - }, - }; - Ok(msg.to_string()) -} diff --git a/applications/launchpad/backend/src/commands/state.rs b/applications/launchpad/backend/src/commands/state.rs deleted file mode 100644 index fbd1a39c4d..0000000000 --- a/applications/launchpad/backend/src/commands/state.rs +++ /dev/null @@ -1,56 +0,0 @@ -use bollard::Docker; -use tauri::PackageInfo; -use tokio::sync::RwLock as AsyncLock; - -// Copyright 2021. The Tari Project -// -// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the -// following conditions are met: -// -// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the -// following disclaimer. -// -// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the -// following disclaimer in the documentation and/or other materials provided with the distribution. -// -// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote -// products derived from this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED -// WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A -// PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY -// DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, -// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER -// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR -// OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH -// DAMAGE. -use crate::docker::{DockerWrapper, Workspaces}; - -/// The state variables that are managed by Tauri. Since the whole app is asynchronous, we need to store -/// * the [`Workspaces`] object. This tracks global config settings, which containers we are running, and what their -/// status is. -/// * A handle to the docker API, via the [`DockerWrapper`] struct. The convenience function [`docker_handle`] gives you -/// a cheap, thread-safe clone to the underlying docker API. -/// * Package info, because Tauri doesn't give us an easy way to get at that in command callbacks for some reason. -/// -/// Things that are mutable (docker, workspaces) are behind an [`AsyncLock`] for thread safety. -pub struct AppState { - pub docker: AsyncLock, - pub workspaces: AsyncLock, - pub package_info: PackageInfo, -} - -impl AppState { - pub fn new(docker: DockerWrapper, workspaces: Workspaces, package_info: PackageInfo) -> Self { - Self { - docker: AsyncLock::new(docker), - workspaces: AsyncLock::new(workspaces), - package_info, - } - } - - pub async fn docker_handle(&self) -> Docker { - let wrapper = self.docker.read().await; - wrapper.handle() - } -} diff --git a/applications/launchpad/backend/src/docker/error.rs b/applications/launchpad/backend/src/docker/error.rs deleted file mode 100644 index dc964b586c..0000000000 --- a/applications/launchpad/backend/src/docker/error.rs +++ /dev/null @@ -1,62 +0,0 @@ -// Copyright 2021. The Tari Project -// -// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the -// following conditions are met: -// -// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following -// disclaimer. -// -// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the -// following disclaimer in the documentation and/or other materials provided with the distribution. -// -// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote -// products derived from this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, -// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR -// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, -// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE -// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -// - -use std::error::Error; - -use tari_common::exit_codes::ExitError; -use thiserror::Error; - -#[derive(Debug, Error)] -pub enum DockerWrapperError { - #[error("Something went wrong with the Docker API")] - DockerError(#[from] bollard::errors::Error), - #[error("Something went wrong on your filesystem")] - FileSystemError(#[from] std::io::Error), - #[error("The requested container id, {0} is not being managed by the wrapper")] - ContainerNotFound(String), - #[error("The designated workspace, {0}, already exists")] - WorkspaceAlreadyExists(String), - #[error("The designated workspace, {0}, does not exist")] - WorkspaceDoesNotExist(String), - #[error("The network is not supported")] - UnsupportedNetwork, - #[error("It should not be possible to be in this error state")] - UnexpectedError, - #[error("Could not create an identity file")] - IdentityError(#[from] ExitError), - #[error("The specified image type is not supported")] - InvalidImageType, -} - -impl DockerWrapperError { - /// Combine all error messages down the chain into one string. - pub fn chained_message(&self) -> String { - let mut messages = vec![self.to_string()]; - let mut this = self as &dyn Error; - while let Some(next) = this.source() { - messages.push(next.to_string()); - this = next; - } - messages.join(" caused by:\n") - } -} diff --git a/applications/launchpad/backend/src/docker/filesystem.rs b/applications/launchpad/backend/src/docker/filesystem.rs deleted file mode 100644 index 538928612b..0000000000 --- a/applications/launchpad/backend/src/docker/filesystem.rs +++ /dev/null @@ -1,71 +0,0 @@ -// Copyright 2021. The Tari Project -// -// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the -// following conditions are met: -// -// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following -// disclaimer. -// -// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the -// following disclaimer in the documentation and/or other materials provided with the distribution. -// -// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote -// products derived from this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, -// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR -// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, -// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE -// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -// - -//! This module does setup and clean up of the host's file system. -//! -//! The docker environment places some files in a volume, e.g. the blockchain db, but other files need to be accessible -//! from the host system, such as logs, and identity files. These all live in a 'workspace' folder, with several sub- -//! directories for the individual components living under it. - -use std::{fs, path::Path}; - -use log::*; -use strum::IntoEnumIterator; - -use crate::docker::{DockerWrapperError, ImageType}; - -/// Creates the folders required for a new workspace off of the given root folder. -/// IF the folders already exist, then nothing happens. -/// On Linux, the permissions are also set to allow world access -pub fn create_workspace_folders>(root: P) -> Result<(), DockerWrapperError> { - if !root.as_ref().exists() { - info!("Creating new workspace at {}", root.as_ref().to_str().unwrap_or("???")); - fs::create_dir_all(&root)?; - } - let make_subfolder = |folder: &str| -> Result<(), std::io::Error> { - let p = root.as_ref().join(folder); - let p_str = p.as_path().to_str().unwrap_or("???"); - if p.exists() { - info!("{} already exists", p_str); - Ok(()) - } else { - info!("Creating new data folder, {}", p_str); - fs::create_dir_all(&p)?; - #[cfg(any(target_os = "linux", target_os = "macos"))] - { - use std::os::unix::fs::PermissionsExt; - let mut perms = std::fs::metadata(&p)?.permissions(); - perms.set_mode(0o777); - fs::set_permissions(&p, perms)?; - } - Ok(()) - } - }; - info!("Making config folder"); - make_subfolder("config")?; - for image in ImageType::iter() { - debug!("Making folder for image:{:?}", image); - make_subfolder(image.data_folder())?; - } - Ok(()) -} diff --git a/applications/launchpad/backend/src/docker/helpers.rs b/applications/launchpad/backend/src/docker/helpers.rs deleted file mode 100644 index 3c32bbb1e9..0000000000 --- a/applications/launchpad/backend/src/docker/helpers.rs +++ /dev/null @@ -1,30 +0,0 @@ -// Copyright 2021. The Tari Project -// -// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the -// following conditions are met: -// -// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following -// disclaimer. -// -// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the -// following disclaimer in the documentation and/or other materials provided with the distribution. -// -// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote -// products derived from this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, -// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR -// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, -// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE -// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -// - -use rand::distributions::{Alphanumeric, Distribution}; - -/// Create a cryptographically secure password on length `len` -pub fn create_password(len: usize) -> String { - let mut rng = rand::thread_rng(); - Alphanumeric.sample_iter(&mut rng).take(len).map(char::from).collect() -} diff --git a/applications/launchpad/backend/src/docker/mod.rs b/applications/launchpad/backend/src/docker/mod.rs deleted file mode 100644 index a1814e2eb9..0000000000 --- a/applications/launchpad/backend/src/docker/mod.rs +++ /dev/null @@ -1,47 +0,0 @@ -// Copyright 2021. The Tari Project -// -// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the -// following conditions are met: -// -// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following -// disclaimer. -// -// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the -// following disclaimer in the documentation and/or other materials provided with the distribution. -// -// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote -// products derived from this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, -// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR -// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, -// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE -// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -// - -mod error; -mod filesystem; -mod models; -mod settings; -mod workspace; -mod wrapper; - -pub mod helpers; - -pub use error::DockerWrapperError; -pub use filesystem::create_workspace_folders; -pub use models::{ContainerId, ContainerState, ContainerStatus, ImageType, LogMessage, TariNetwork}; -pub use settings::{ - BaseNodeConfig, - LaunchpadConfig, - MmProxyConfig, - Sha3MinerConfig, - WalletConfig, - XmRigConfig, - DEFAULT_MINING_ADDRESS, - DEFAULT_MONEROD_URL, -}; -pub use workspace::{TariWorkspace, Workspaces}; -pub use wrapper::DockerWrapper; diff --git a/applications/launchpad/backend/src/docker/models.rs b/applications/launchpad/backend/src/docker/models.rs deleted file mode 100644 index a730a2b31d..0000000000 --- a/applications/launchpad/backend/src/docker/models.rs +++ /dev/null @@ -1,261 +0,0 @@ -// Copyright 2021. The Tari Project -// -// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the -// following conditions are met: -// -// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following -// disclaimer. -// -// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the -// following disclaimer in the documentation and/or other materials provided with the distribution. -// -// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote -// products derived from this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, -// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR -// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, -// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE -// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -// - -use std::{ - convert::TryFrom, - fmt::{Display, Formatter}, -}; - -use bollard::{container::LogOutput, models::ContainerCreateResponse}; -use serde::{Deserialize, Serialize}; -use strum_macros::EnumIter; - -use crate::docker::DockerWrapperError; - -//------------------------------------------- ContainerId ---------------------------------------------- -#[derive(Debug, Clone, PartialEq, Eq, Hash)] -pub struct ContainerId(pub String); - -impl From for ContainerId { - fn from(s: String) -> Self { - Self(s) - } -} - -impl AsRef for ContainerId { - fn as_ref(&self) -> &str { - self.0.as_str() - } -} - -impl Display for ContainerId { - fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { - f.write_str(self.0.as_str()) - } -} - -impl ContainerId { - pub fn as_str(&self) -> &str { - self.0.as_str() - } -} - -//------------------------------------------- ContainerStatus ---------------------------------------------- - -#[derive(Debug, PartialEq, Eq, Clone, Copy)] -pub enum ContainerStatus { - Created, - Running, - Stopped, - Deleted, -} - -//------------------------------------------- ContainerState ---------------------------------------------- - -pub struct ContainerState { - name: String, - id: ContainerId, - info: ContainerCreateResponse, - status: ContainerStatus, -} - -impl ContainerState { - pub fn new(name: String, id: ContainerId, info: ContainerCreateResponse) -> Self { - Self { - name, - id, - info, - status: ContainerStatus::Created, - } - } - - pub fn running(&mut self) { - self.status = ContainerStatus::Running; - } - - pub fn set_stop(&mut self) { - self.status = ContainerStatus::Stopped; - } - - pub fn set_deleted(&mut self) { - self.status = ContainerStatus::Deleted; - } - - pub fn name(&self) -> &str { - self.name.as_str() - } - - pub fn info(&self) -> &ContainerCreateResponse { - &self.info - } - - pub fn id(&self) -> &ContainerId { - &self.id - } - - pub fn status(&self) -> ContainerStatus { - self.status - } -} - -//------------------------------------------- LogMessage ---------------------------------------------- - -#[derive(Debug, Clone, Serialize, Deserialize)] -pub struct LogMessage { - pub message: String, - pub source: String, -} - -impl From for LogMessage { - fn from(log: LogOutput) -> Self { - let (source, message) = match log { - LogOutput::StdErr { message } => ("StdErr".to_string(), String::from_utf8_lossy(&message).into_owned()), - LogOutput::StdOut { message } => ("StdOut".to_string(), String::from_utf8_lossy(&message).into_owned()), - LogOutput::Console { message } => ("Console".to_string(), String::from_utf8_lossy(&message).into_owned()), - LogOutput::StdIn { message } => ("StdIn".to_string(), String::from_utf8_lossy(&message).into_owned()), - }; - Self { source, message } - } -} - -//------------------------------------------- TariNetwork ---------------------------------------------- - -/// Supported networks for the launchpad -#[derive(Serialize, Debug, Deserialize, Clone, Copy)] -pub enum TariNetwork { - Dibbler, - Igor, - Mainnet, -} - -impl TariNetwork { - pub fn lower_case(self) -> &'static str { - match self { - Self::Dibbler => "dibbler", - Self::Igor => "igor", - Self::Mainnet => "mainnet", - } - } - - pub fn upper_case(self) -> &'static str { - match self { - Self::Dibbler => "DIBBLER", - Self::Igor => "IGOR", - Self::Mainnet => "MAINNET", - } - } -} - -/// Default network is Dibbler. This will change after mainnet launch -impl Default for TariNetwork { - fn default() -> Self { - Self::Dibbler - } -} - -impl TryFrom<&str> for TariNetwork { - type Error = DockerWrapperError; - - fn try_from(value: &str) -> Result { - match value { - "dibbler" => Ok(TariNetwork::Dibbler), - "igor" => Ok(TariNetwork::Igor), - "mainnet" => Ok(TariNetwork::Mainnet), - _ => Err(DockerWrapperError::UnsupportedNetwork), - } - } -} - -//------------------------------------------- ImageType ---------------------------------------------- - -#[derive(Debug, Clone, Copy, EnumIter, PartialEq, Eq, Hash, Serialize)] -pub enum ImageType { - Tor, - BaseNode, - Wallet, - XmRig, - Sha3Miner, - MmProxy, - Monerod, - Frontail, -} - -impl ImageType { - pub fn image_name(&self) -> &str { - match self { - Self::Tor => "tor", - Self::BaseNode => "tari_base_node", - Self::Wallet => "tari_console_wallet", - Self::XmRig => "xmrig", - Self::Sha3Miner => "tari_sha3_miner", - Self::MmProxy => "tari_mm_proxy", - Self::Monerod => "monerod", - Self::Frontail => "frontail", - } - } - - pub fn container_name(&self) -> &str { - match self { - Self::Tor => "tor", - Self::BaseNode => "base_node", - Self::Wallet => "wallet", - Self::XmRig => "xmrig", - Self::Sha3Miner => "sha3_miner", - Self::MmProxy => "mm_proxy", - Self::Monerod => "monerod", - Self::Frontail => "frontail", - } - } - - pub fn data_folder(&self) -> &str { - match self { - Self::Tor => "tor", - Self::BaseNode => "base_node", - Self::Wallet => "wallet", - Self::XmRig => "xmrig", - Self::Sha3Miner => "sha3_miner", - Self::MmProxy => "mm_proxy", - Self::Monerod => "monerod", - Self::Frontail => "frontail", - } - } -} - -impl TryFrom<&str> for ImageType { - type Error = DockerWrapperError; - - fn try_from(value: &str) -> Result { - let s = value.to_lowercase(); - match s.as_str() { - "tor" => Ok(Self::Tor), - "base_node" | "base node" => Ok(Self::BaseNode), - "wallet" => Ok(Self::Wallet), - "xmrig" => Ok(Self::XmRig), - "sha3_miner" | "sha3 miner" => Ok(Self::Sha3Miner), - "mm_proxy" | "mm proxy" => Ok(Self::MmProxy), - "monerod" | "monero" => Ok(Self::Monerod), - "frontail" => Ok(Self::Frontail), - _ => Err(DockerWrapperError::InvalidImageType), - } - } -} diff --git a/applications/launchpad/backend/src/docker/settings.rs b/applications/launchpad/backend/src/docker/settings.rs deleted file mode 100644 index f74b1e3521..0000000000 --- a/applications/launchpad/backend/src/docker/settings.rs +++ /dev/null @@ -1,531 +0,0 @@ -// Copyright 2021. The Tari Project -// -// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the -// following conditions are met: -// -// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following -// disclaimer. -// -// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the -// following disclaimer in the documentation and/or other materials provided with the distribution. -// -// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote -// products derived from this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, -// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR -// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, -// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE -// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -// - -use std::{collections::HashMap, path::PathBuf, time::Duration}; - -use bollard::models::{Mount, MountTypeEnum, PortBinding, PortMap}; -use config::ConfigError; -use derivative::Derivative; -use serde::{Deserialize, Serialize}; -use thiserror::Error; -use tor_hash_passwd::EncryptedKey; - -use crate::docker::{models::ImageType, TariNetwork}; - -// TODO get a proper mining address for each network -pub const DEFAULT_MINING_ADDRESS: &str = - "5AJ8FwQge4UjT9Gbj4zn7yYcnpVQzzkqr636pKto59jQcu85CFsuYVeFgbhUdRpiPjUCkA4sQtWApUzCyTMmSigFG2hDo48"; - -pub const DEFAULT_MONEROD_URL: &str = "http://stagenet.xmr-tw.org:38081,\ -http://stagenet.community.xmr.to:38081,\ -http://monero-stagenet.exan.tech:38081,\ -http://xmr-lux.boldsuck.org:38081,\ -http://singapore.node.xmr.pm:38081"; - -#[derive(Default, Debug, Serialize, Deserialize)] -pub struct BaseNodeConfig { - /// The time delay before starting the container and running the base node executable - pub delay: Duration, -} - -#[derive(Default, Derivative, Serialize, Deserialize)] -#[derivative(Debug)] -pub struct WalletConfig { - /// The time delay before starting the container and running the wallet executable - pub delay: Duration, - /// The password to de/en-crypt the wallet database - #[serde(skip_serializing)] - #[derivative(Debug = "ignore")] - pub password: String, -} - -#[derive(Default, Debug, Serialize, Deserialize)] -pub struct XmRigConfig { - /// The time delay before starting the container and running the monero miner executable - pub delay: Duration, - /// The address that will accept Monero mining rewards - pub monero_mining_address: String, -} - -#[derive(Default, Debug, Serialize, Deserialize)] -pub struct Sha3MinerConfig { - /// The time delay before starting the container and running the SHA3 CPU miner executable - pub delay: Duration, - /// The number of threads to employ for SHA3 mining - pub num_mining_threads: usize, -} - -#[derive(Derivative, Serialize, Deserialize)] -#[derivative(Debug)] -pub struct MmProxyConfig { - /// The time delay before starting the container and running the proxy executable - pub delay: Duration, - /// A URL specifying the Monero daemon to connect to - pub monerod_url: String, - /// If required, the monero username for the monero daemon - pub monero_username: String, - /// If required, the password needed to access the monero deamon - #[serde(skip_serializing)] - #[derivative(Debug = "ignore")] - pub monero_password: String, - /// If true, provide the monero username and password to the daemon. Otherwise those strings are ignored. - pub monero_use_auth: bool, -} - -impl Default for MmProxyConfig { - fn default() -> Self { - MmProxyConfig { - delay: Duration::from_secs(5), - monerod_url: DEFAULT_MONEROD_URL.to_string(), - monero_username: "".to_string(), - monero_password: "".to_string(), - monero_use_auth: false, - } - } -} - -impl MmProxyConfig { - pub fn monero_use_auth(&self) -> usize { - if self.monero_use_auth { - 1 - } else { - 0 - } - } -} - -/// Tari Launchpad configuration struct. This will generally be populated from some front-end or persistent storage -/// file and is used to generate the environment variables needed to configure and run the various docker containers. -#[derive(Default, Debug, Serialize, Deserialize)] -pub struct LaunchpadConfig { - /// The directory to use for config, id files and logs - pub data_directory: PathBuf, - /// The Tari network to use. Default = dibbler - pub tari_network: TariNetwork, - /// The tor control password to share among containers. - pub tor_control_password: String, - /// Whether to spin up a base node or not, with the given configuration. Usually you want this. - pub base_node: Option, - /// Whether to spin up a console wallet daemon, with the given configuration. Optional. - pub wallet: Option, - /// Whether to spin up a SHA3 miner or not, with the given configuration. If you want to mine Tari natively, - /// include this. - pub sha3_miner: Option, - /// Whether to spin up a merge-mine proxy or not, with the given configuration. If included, you must also include - /// xmrig - pub mm_proxy: Option, - /// Whether to spin up a Monero miner or not, with the given configuration. If included you should also include - /// mm_proxy - pub xmrig: Option, - /// The Docker registry to use to download images. By default we use quay.io - pub registry: Option, - /// The docker tag to use. By default, we use 'latest' - pub tag: Option, -} - -impl LaunchpadConfig { - pub fn load() -> Result { - unimplemented!() - } - - /// Returns a list of environment variables that need to be set in the running container. For Tari containers, we - /// use this to override settings in the `config.yml` file that are generated on the fly here (the tor control - /// port password for example). - pub fn environment(&self, image_type: ImageType) -> Vec { - match image_type { - ImageType::BaseNode => self.base_node_environment(), - ImageType::Wallet => self.wallet_environment(), - ImageType::XmRig => self.xmrig_environment(), - ImageType::Sha3Miner => self.sha3_miner_environment(), - ImageType::MmProxy => self.mm_proxy_environment(), - ImageType::Tor => self.tor_environment(), - ImageType::Monerod => self.monerod_environment(), - ImageType::Frontail => self.common_envars(), - } - } - - /// Provides a hashmap that bollard needs to mount the volumes we want for each image type. - pub fn volumes(&self, image_type: ImageType) -> HashMap> { - match image_type { - ImageType::BaseNode => self.build_volumes(true, true), - ImageType::Wallet => self.build_volumes(true, false), - ImageType::XmRig => self.build_volumes(true, false), - ImageType::Sha3Miner => self.build_volumes(true, false), - ImageType::MmProxy => self.build_volumes(true, false), - ImageType::Tor => self.build_volumes(false, false), - ImageType::Monerod => self.build_volumes(false, false), - ImageType::Frontail => self.build_volumes(true, false), - } - } - - /// Similar to [`volumes`], provides a bollard configuration for mounting volumes. - pub fn mounts(&self, image_type: ImageType, volume_name: String) -> Vec { - match image_type { - ImageType::BaseNode => self.build_mounts(true, true, volume_name), - ImageType::Wallet => self.build_mounts(true, true, volume_name), - ImageType::XmRig => self.build_mounts(false, true, volume_name), - ImageType::Sha3Miner => self.build_mounts(false, true, volume_name), - ImageType::MmProxy => self.build_mounts(false, true, volume_name), - ImageType::Tor => self.build_mounts(false, false, volume_name), - ImageType::Monerod => self.build_mounts(false, false, volume_name), - ImageType::Frontail => self.build_mounts(false, true, volume_name), - } - } - - fn build_mounts(&self, blockchain: bool, general: bool, volume_name: String) -> Vec { - let mut mounts = Vec::with_capacity(2); - if general { - #[cfg(target_os = "windows")] - let host = format!( - "//{}", - self.data_directory - .iter() - .filter_map(|part| { - use std::{ffi::OsStr, path}; - - use regex::Regex; - - if part == OsStr::new(&path::MAIN_SEPARATOR.to_string()) { - None - } else { - let drive = Regex::new(r"(?P[A-Za-z]):").unwrap(); - let part = part.to_string_lossy().to_string(); - if drive.is_match(part.as_str()) { - Some(drive.replace(part.as_str(), "$letter").to_lowercase()) - } else { - Some(part) - } - } - }) - .collect::>() - .join("/") - ); - #[cfg(target_os = "macos")] - let host = format!("/host_mnt{}", self.data_directory.to_string_lossy()); - #[cfg(target_os = "linux")] - let host = self.data_directory.to_string_lossy().to_string(); - let mount = Mount { - target: Some("/var/tari".to_string()), - source: Some(host), - typ: Some(MountTypeEnum::BIND), - bind_options: None, - ..Default::default() - }; - mounts.push(mount); - } - if blockchain { - let mount = Mount { - target: Some("/blockchain".to_string()), - source: Some(volume_name), - typ: Some(MountTypeEnum::VOLUME), - volume_options: None, - ..Default::default() - }; - mounts.push(mount); - } - mounts - } - - /// Returns a map of ports to expose to the host system. TODO - remove the hardcoding so that multiple workspaces - /// don't have colliding exposed ports. - pub fn ports(&self, image_type: ImageType) -> HashMap> { - match image_type { - ImageType::BaseNode => create_port_map(&["18142", "18189"]), - ImageType::Wallet => create_port_map(&["18143", "18188"]), - ImageType::XmRig => create_port_map(&[]), - ImageType::Sha3Miner => create_port_map(&[]), - ImageType::MmProxy => create_port_map(&[]), - ImageType::Tor => create_port_map(&[]), - ImageType::Monerod => create_port_map(&[]), - ImageType::Frontail => create_port_map(&["18130"]), - } - } - - /// As for [`ports`] returns a bollard configuration for port mappings. - pub fn port_map(&self, image_type: ImageType) -> PortMap { - let ports = self.ports(image_type); - ports - .into_iter() - .map(|(k, _)| { - let binding = vec![PortBinding { - host_ip: Some("".to_string()), - host_port: Some(k.clone()), - }]; - (k, Some(binding)) - }) - .collect() - } - - /// Return the command line arguments we want for the given container execution. - pub fn command(&self, image_type: ImageType) -> Vec { - match image_type { - ImageType::BaseNode => self.base_node_cmd(), - ImageType::Wallet => self.wallet_cmd(), - ImageType::XmRig => self.xmrig_cmd(), - ImageType::Sha3Miner => self.miner_cmd(), - ImageType::MmProxy => self.mm_proxy_cmd(), - ImageType::Tor => self.tor_cmd(), - ImageType::Monerod => self.monerod_cmd(), - ImageType::Frontail => self.frontail_cmd(), - } - } - - /// Returns the canonical path to the id files. The canonical path is defined as - /// `{root_path}/{image_data_folder}/config/{network}/{image_type}_id.json` - pub fn id_path(&self, root_path: &str, image_type: ImageType) -> Option { - match image_type { - ImageType::BaseNode | ImageType::Wallet => Some( - PathBuf::from(root_path) - .join(image_type.data_folder()) - .join("config") - .join(self.tari_network.lower_case()) - .join(format!("{}_id.json", image_type.image_name())), - ), - _ => None, - } - } - - fn frontail_cmd(&self) -> Vec { - let args = vec![ - "-p", - "18130", - "base_node/log/core.log", - "wallet/log/core.log", - "sha3_miner/log/core.log", - "mm_proxy/log/core.log", - ]; - args.into_iter().map(String::from).collect() - } - - fn base_node_cmd(&self) -> Vec { - let args = vec!["--non-interactive-mode", "--log-config=/var/tari/config/log4rs.yml"]; - args.into_iter().map(String::from).collect() - } - - fn wallet_cmd(&self) -> Vec { - let args = vec!["--non-interactive-mode", "--log-config=/var/tari/config/log4rs.yml"]; - args.into_iter().map(String::from).collect() - } - - fn miner_cmd(&self) -> Vec { - let args = vec!["--log-config=/var/tari/config/log4rs.yml"]; - args.into_iter().map(String::from).collect() - } - - fn mm_proxy_cmd(&self) -> Vec { - let args = vec!["--log-config=/var/tari/config/log4rs.yml"]; - args.into_iter().map(String::from).collect() - } - - fn xmrig_cmd(&self) -> Vec { - let args = vec![ - "--url=mm_proxy:18081", - "--user=${TARI_MONERO_WALLET_ADDRESS}", - "--coin=monero", - "--daemon", - "--log-file=/var/tari/xmrig/xmrig.log", - "--verbose", - ]; - args.into_iter().map(String::from).collect() - } - - fn monerod_cmd(&self) -> Vec { - let network = match self.tari_network { - TariNetwork::Mainnet => "--mainnet", - _ => "--stagenet", - }; - let args = vec![ - "--non-interactive", - "--restricted-rpc", - "--rpc-bind-ip=0.0.0.0", - "--confirm-external-bind", - "--enable-dns-blocklist", - "--log-file=/home/monerod/monerod.log", - "--fast-block-sync=1", - "--prune-blockchain", - network, - ]; - args.into_iter().map(String::from).collect() - } - - fn tor_cmd(&self) -> Vec { - let hashed_password = EncryptedKey::hash_password(self.tor_control_password.as_str()).to_string(); - let args = vec![ - "/usr/bin/tor", - "--SocksPort", - "0.0.0.0:9050", - "--ControlPort", - "0.0.0.0:9051", - "--CookieAuthentication", - "0", - "--ClientOnly", - "1", - "--ClientUseIPv6", - "1", - "--HashedControlPassword", - hashed_password.as_str(), - ]; - args.into_iter().map(String::from).collect() - } - - /// Returns the bollard configuration map. You can specify any/all of the host-mounted data folder, of the - /// blockchain folder to map. - pub fn build_volumes(&self, general: bool, tari_blockchain: bool) -> HashMap> { - let mut volumes = HashMap::new(); - if general { - volumes.insert("/var/tari".to_string(), HashMap::<(), ()>::new()); - } - if tari_blockchain { - volumes.insert("/blockchain".to_string(), HashMap::new()); - } - volumes - } - - fn common_envars(&self) -> Vec { - vec![ - format!("TARI_NETWORK={}", self.tari_network.lower_case()), - format!("DATA_FOLDER={}", self.data_directory.to_str().unwrap_or("")), // TODO deal with None - "TARI_LOG_CONFIGURATION=/var/tari/config/log4rs.yml".to_string(), - "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin".to_string(), - ] - } - - fn base_node_tor_config(&self, env: &mut Vec) { - env.append(&mut vec![format!( - "TARI_BASE_NODE__P2P__TRANSPORT__TOR__CONTROL_AUTH=password={}", - self.tor_control_password - )]); - } - - /// Generate the vector of ENVAR strings for the docker environment - fn base_node_environment(&self) -> Vec { - let mut env = self.common_envars(); - self.base_node_tor_config(&mut env); - if let Some(base_node) = &self.base_node { - env.append(&mut vec![ - format!("WAIT_FOR_TOR={}", base_node.delay.as_secs()), - format!( - "TARI_BASE_NODE__DATA_DIR=/blockchain/{}", - self.tari_network.lower_case() - ), - "APP_NAME=base_node".to_string(), - ]); - } - env - } - - fn wallet_environment(&self) -> Vec { - let mut env = self.common_envars(); - if let Some(config) = &self.wallet { - env.append(&mut vec![ - "APP_NAME=wallet".to_string(), - "APP_EXEC=tari_console_wallet".to_string(), - format!("WAIT_FOR_TOR={}", config.delay.as_secs() + 3), - "SHELL=/bin/bash".to_string(), - "TERM=linux".to_string(), - format!("TARI_WALLET_PASSWORD={}", config.password), - format!( - "TARI_WALLET__P2P__TRANSPORT__TOR__CONTROL_AUTH=password={}", - self.tor_control_password - ), - ]); - } - env - } - - fn xmrig_environment(&self) -> Vec { - let mut env = self.common_envars(); - let address = match &self.xmrig { - Some(config) if config.monero_mining_address.len() > 12 => config.monero_mining_address.as_str(), - _ => DEFAULT_MINING_ADDRESS, - }; - if let Some(config) = &self.xmrig { - env.append(&mut vec![ - format!("WAIT_FOR_TOR={}", config.delay.as_secs() + 9), - format!("TARI_MONERO_WALLET_ADDRESS={}", address), - ]); - } - env - } - - fn sha3_miner_environment(&self) -> Vec { - let mut env = self.common_envars(); - self.base_node_tor_config(&mut env); - if let Some(config) = &self.sha3_miner { - env.append(&mut vec![ - format!("WAIT_FOR_TOR={}", config.delay.as_secs() + 6), - "APP_NAME: sha3_miner".to_string(), - "APP_EXEC: tari_miner".to_string(), - format!("TARI_MINER__NUM_MINING_THREADS: {}", config.num_mining_threads), - "TARI_MINER__MINE_ON_TIP_ONLY: 1".to_string(), - // This setting should be made obsolete soon: - format!( - "TARI_BASE_NODE__{}__BASE_NODE_GRPC_ADDRESS=/dns4/base_node/tcp/18142", - self.tari_network.upper_case() - ), - format!( - "TARI_BASE_NODE__{}__GRPC_BASE_NODE_ADDRESS=/dns4/base_node/tcp/18142", - self.tari_network.upper_case() - ), - "TARI_WALLET__GRPC_ADDRESS=/dns4/wallet/tcp/18143".to_string(), - ]); - } - env - } - - fn mm_proxy_environment(&self) -> Vec { - let mut env = self.common_envars(); - self.base_node_tor_config(&mut env); - if let Some(config) = &self.mm_proxy { - env.append(&mut vec![ - format!("WAIT_FOR_TOR={}", config.delay.as_secs() + 6), - "APP_NAME=mm_proxy".to_string(), - "APP_EXEC=tari_merge_mining_proxy".to_string(), - format!("TARI_MERGE_MINING_PROXY__MONEROD_URL={}", config.monerod_url), - format!("TARI_MERGE_MINING_PROXY__MONEROD_USERNAME={}", config.monero_username), - format!("TARI_MERGE_MINING_PROXY__MONEROD_PASSWORD={}", config.monero_password), - format!("TARI_MERGE_MINING_PROXY__MONEROD_USE_AUTH={}", config.monero_use_auth()), - ]); - } - env - } - - fn tor_environment(&self) -> Vec { - self.common_envars() - } - - fn monerod_environment(&self) -> Vec { - self.common_envars() - } -} - -#[derive(Debug, Error)] -pub enum LaunchpadConfigError {} - -fn create_port_map(ports: &[&'static str]) -> HashMap> { - let mut result = HashMap::new(); - for &port in ports { - result.insert(format!("{}/tcp", port), HashMap::new()); - } - result -} diff --git a/applications/launchpad/backend/src/docker/workspace.rs b/applications/launchpad/backend/src/docker/workspace.rs deleted file mode 100644 index e013001004..0000000000 --- a/applications/launchpad/backend/src/docker/workspace.rs +++ /dev/null @@ -1,624 +0,0 @@ -// Copyright 2021. The Tari Project -// -// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the -// following conditions are met: -// -// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following -// disclaimer. -// -// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the -// following disclaimer in the documentation and/or other materials provided with the distribution. -// -// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote -// products derived from this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, -// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR -// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, -// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE -// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -// - -use std::collections::HashMap; - -use bollard::{ - container::{ - Config, - CreateContainerOptions, - LogsOptions, - NetworkingConfig, - Stats, - StatsOptions, - StopContainerOptions, - }, - models::{ContainerCreateResponse, EndpointSettings, HostConfig, Network}, - network::{ConnectNetworkOptions, CreateNetworkOptions, InspectNetworkOptions}, - volume::CreateVolumeOptions, - Docker, -}; -use futures::{Stream, StreamExt, TryStreamExt}; -use log::*; -use strum::IntoEnumIterator; -use tari_app_utilities::identity_management::setup_node_identity; -use tari_comms::{peer_manager::PeerFeatures, NodeIdentity}; - -use crate::docker::{ - models::{ContainerId, ContainerState}, - DockerWrapperError, - ImageType, - LaunchpadConfig, - LogMessage, -}; - -static DEFAULT_REGISTRY: &str = "quay.io/tarilabs"; -static DEFAULT_TAG: &str = "latest"; - -/// `Workspaces` allows us to spin up multiple [TariWorkspace] recipes or configurations at once. Most users will only -/// ever need one at a time, and the default one at that, but developers and testers will likely find this useful. -/// -/// For example, you might want to run a node and wallet on two different networks concurrently. -/// Or, you might want to spin up two wallets on the same network to test some new feature. Here you would have one -/// recipe with just a base node and a wallet, and another recipe with just a wallet. -/// -/// Workspaces are referenced by a name, which is an arbitrary string, but by convention, you'll want to keep workspace -/// names short and do not use spaces in workspace names. -/// -/// Docker containers are named using a `{workspace_name}_{image_type}` convention. See [TariWorkspace] for more -/// details. -/// -/// Each workspace should also have a unique data folder to keep logs and configuration separate, but this is not a hard -/// requirement. -#[derive(Default)] -pub struct Workspaces { - workspaces: HashMap, -} - -impl Workspaces { - /// Returns a mutable reference to the `name`d workspace. For an immutable reference, see [workspace_mut]. - pub fn get_workspace_mut(&mut self, name: &str) -> Option<&mut TariWorkspace> { - self.workspaces.get_mut(name) - } - - /// Returns an immtable reference to the `name`d workspace. For a mutable reference, see [get_workspace_mut]. - pub fn get_workspace(&self, name: &str) -> Option<&TariWorkspace> { - self.workspaces.get(name) - } - - /// Checks if a workspace with the given name exists. - pub fn workspace_exists(&self, name: &str) -> bool { - self.workspaces.contains_key(name) - } - - /// Create a new Tari workspace. It must not previously exist - pub fn create_workspace(&mut self, name: &str, config: LaunchpadConfig) -> Result<(), DockerWrapperError> { - if self.workspaces.contains_key(name) { - return Err(DockerWrapperError::WorkspaceAlreadyExists(name.to_string())); - } - let new_system = TariWorkspace::new(name, config); - self.workspaces.insert(name.to_string(), new_system); - Ok(()) - } - - /// Gracefully shut down the docker images and delete them - /// The volumes are kept, since if we restart, we don't want to re-sync the entire blockchain again - pub async fn shutdown(&mut self, docker: &Docker) -> Result<(), DockerWrapperError> { - for (name, system) in &mut self.workspaces { - info!("Shutting down {}", name); - system.stop_containers(true, docker).await; - } - Ok(()) - } -} - -/// A TariWorkspace is a configuration of docker images (node, wallet, miner etc), configuration files and secrets, -/// and log files. -/// -/// ### Data locations -/// A workspace manages data in several places -/// * **Blockchain data** is stored in a [Docker volume](https://docs.docker.com/storage/volumes/) that is not directly -/// accessible to the host system. This is primarily done for performance reasons, but should not be a major issue -/// since you seldom need direct access to the LMDB database. If you do need the database for some reason, you can -/// mount the volume and run a bash command, for instance: `docker run --rm -v $(pwd):/backup -v -/// blockchain:/blockchain ubuntu tar czvf /backup/backup.tar.gz /blockchain`. As currently written, the blockchain -/// data is namespaced by workspace but it is also possible in principle for different workspaces to (TODO) share -/// blockchain data (for the same network), thereby preventing the need to keep multiple copies of the blockchain. In -/// this case, you need to be careful not to introduce contention from multiple nodes running against the same LMDB -/// database, leading to corrupt db files. -/// * **Configuration** and log files are stored of the workspace `root_folder`. A typical folder structure looks like -/// ```text -/// {root_folder} -/// ├── base_node -/// │ └── log -/// │ ├── core.log -/// │ ├── network.log -/// │ └── other.log -/// ├── config -/// │ ├── config.toml -/// │ ├── log4rs.yml -/// │ └── dibbler -/// │ ├── base_node_tor.json -/// │ ├── tari_base_node_id.json -/// │ └── tari_console_wallet_id.json -/// ├── mm_proxy -/// ├── monerod -/// ├── sha3_miner -/// │ └── log -/// │ ├── core.log -/// │ ├── network.log -/// │ └── other.log -/// ├── tor -/// ├── wallet -/// │ ├── log -/// │ │ ├── core.log -/// │ │ ├── network.log -/// │ │ └── other.log -/// │ └── wallet -/// │ ├── console-wallet.dat -/// │ ├── console-wallet.dat-shm -/// │ └── console-wallet.dat-wal -/// └── xmrig -/// ``` -/// the `{root_folder}` is mounted into the docker filesystem and is accessible to all containers in the workspace. -/// TODO - investigate security issues for this. In particular, we almost certainly want to isolate the wallet data -/// This means that changes to `config/log4rs.yml` take effect in all running containers immediately. -/// * **Docker containers** are ephemeral and are created and destroyed at will. -/// -/// ### Networking -/// Tor is used by several other containers. The control password for Tor is randomly generated when the workspace is -/// set up, and shared as an environment variable between containers. -/// -/// A dedicated, namespaced network is created for each workspace. This means that each container in the network can -/// talk to any other container in the same workspace, but not to containers in other workspaces. -/// -/// We also expose some ports to the host system for host-container communication. -/// -/// * Base node - ports 18142 and 18149. TODO - currently the same port is mapped to the host. For multiple Workspaces -/// we'd need to expose a different port to the host. e.g. 1n142 where n is the workspace number. -/// * Wallet - 18143 and 18188 -/// * Frontail - 18130 -pub struct TariWorkspace { - name: String, - config: LaunchpadConfig, - containers: HashMap, -} - -impl TariWorkspace { - /// Create a new Tari system using the provided configuration - pub fn new(name: &str, config: LaunchpadConfig) -> Self { - Self { - name: name.to_string(), - config, - containers: HashMap::new(), - } - } - - /// Return a reference to the immutable configuration object for this workspace. - pub fn config(&self) -> &LaunchpadConfig { - &self.config - } - - /// Returns the name of this system, which is generally used as the workspace name when running multiple Tari - /// systems - pub fn name(&self) -> &str { - self.name.as_str() - } - - /// Returns the full image name for the given image type. for the default registry this is typically something like - /// `quay.io/tarilabs/tari_base_node:latest`. One should be able to use this string to do a successful - /// `docker pull {image_name}`. - /// - /// It also lets power users customise which version of docker images they want to run in the workspace. - pub fn fully_qualified_image(image: ImageType, registry: Option<&str>, tag: Option<&str>) -> String { - let reg = registry.unwrap_or(DEFAULT_REGISTRY); - let tag = Self::arch_specific_tag(tag); - format!("{}/{}:{}", reg, image.image_name(), tag) - } - - /// Returns an architecture-specific tag based on the current CPU and the given label. e.g. - /// `arch_specific_tag(Some("v1.0"))` returns `"v1.0-arm64"` on M1 chips, and `v1.0-amd64` on Intel and AMD chips. - pub fn arch_specific_tag(label: Option<&str>) -> String { - let label = label.unwrap_or(DEFAULT_TAG); - let platform = match std::env::consts::ARCH { - "x86_64" => "amd64", - "aarch64" => "arm64", - _ => "unsupported", - }; - format!("{}-{}", label, platform) - } - - /// Starts the Tari workspace recipe. - /// - /// This is an MVP / PoC version that starts everything in one go, but TODO, should really take some sort of recipe - /// object to allow us to build up different recipes (wallet only, full miner, SHA3-mining only etc) - pub async fn start_recipe(&mut self, docker: Docker) -> Result<(), DockerWrapperError> { - // Create or load identities - let _ids = self.create_or_load_identities()?; - // Set up the local network - if !self.network_exists(&docker).await? { - self.create_network(&docker).await?; - } - // Create or restart the volume - - let registry = self.config.registry.clone(); - let tag = self.config.tag.clone(); - for image in self.images_to_start() { - // Start each container - let name = self - .start_service(image, registry.clone(), tag.clone(), docker.clone()) - .await?; - info!( - "Docker container {} ({}) successfully started", - image.image_name(), - name - ); - } - Ok(()) - } - - /// Bootstraps a node identity for the container, typically a base node or wallet instance. If an identity file - /// already exists at the canonical path location, it is loaded and returned instead. - /// - /// The canonical path is defined as `{root_path}/{image_type}/config/{network}/{image_type}_id.json` - pub fn create_or_load_identity( - &self, - root_path: &str, - image: ImageType, - ) -> Result, DockerWrapperError> { - if let Some(id_file_path) = self.config.id_path(root_path, image) { - debug!("Loading or creating identity file {}", id_file_path.to_string_lossy()); - let id = setup_node_identity(id_file_path, None, true, PeerFeatures::COMMUNICATION_NODE)? - .as_ref() - .clone(); - Ok(Some(id)) - } else { - Ok(None) - } - } - - /// A convenience method that calls [create_or_load_identity] for each image type. - pub fn create_or_load_identities(&self) -> Result, DockerWrapperError> { - let root_path = self.config.data_directory.to_string_lossy().to_string(); - let mut ids = HashMap::new(); - for image in ImageType::iter() { - if let Some(id) = self.create_or_load_identity(root_path.as_str(), image)? { - let _node_identity = ids.insert(image, id); - } - } - Ok(ids) - } - - /// Create and return a [`Stream`] of [`LogMessage`] instances for the `name`d container in the workspace. - pub fn logs( - &self, - container_name: &str, - docker: &Docker, - ) -> Option>> { - let options = LogsOptions:: { - follow: true, - stdout: true, - stderr: true, - ..Default::default() - }; - self.containers.get(container_name).map(move |container| { - let id = container.id(); - docker - .logs(id.as_str(), Some(options)) - .map(|log| log.map(LogMessage::from).map_err(DockerWrapperError::from)) - }) - } - - /// Returns a [`Stream`] of resource stats for the container `name`, if it exists - pub fn resource_stats( - &self, - name: &str, - docker: &Docker, - ) -> Option>> { - if let Some(container) = self.containers.get(name) { - let options = StatsOptions { - stream: true, - one_shot: false, - }; - let id = container.id(); - let stream = docker - .stats(id.as_str(), Some(options)) - .map_err(DockerWrapperError::from); - Some(stream) - } else { - None - } - } - - /// Create and run a docker container. - /// - /// ## Arguments - /// * `image`: The type of image to start. See [`ImageType`]. - /// * `registry`: An optional docker registry path to use. The default is `quay.io/tarilabs` - /// * `tag`: The image tag to use. The default is `latest`. - /// * `docker`: a [`Docker`] instance. - /// - /// ## Return - /// - /// The method returns a future that resolves to a [`DockerWrapperError`] on an error, or the container name on - /// success. - /// - /// `start_service` creates a new docker container and runs it. As part of this process, - /// * it pulls configuration data from the [`LaunchConfig`] instance attached to this [`DockerWRapper`] to construct - /// the Environment, Volume configuration, and exposed Port configuration. - /// * creates a new container - /// * starts the container - /// * adds the container reference to the current list of containers being managed - /// * Returns the container name - pub async fn start_service( - &mut self, - image: ImageType, - registry: Option, - tag: Option, - docker: Docker, - ) -> Result { - let args = self.config.command(image); - let image_name = TariWorkspace::fully_qualified_image(image, registry.as_deref(), tag.as_deref()); - let options = Some(CreateContainerOptions { - name: format!("{}_{}", self.name, image.image_name()), - }); - let envars = self.config.environment(image); - let volumes = self.config.volumes(image); - let ports = self.config.ports(image); - let port_map = self.config.port_map(image); - let mounts = self.config.mounts(image, self.tari_blockchain_volume_name()); - let mut endpoints = HashMap::new(); - let endpoint = EndpointSettings { - aliases: Some(vec![image.container_name().to_string()]), - ..Default::default() - }; - endpoints.insert(self.network_name(), endpoint); - let config = Config:: { - image: Some(image_name.clone()), - attach_stdin: Some(false), - attach_stdout: Some(false), - attach_stderr: Some(false), - exposed_ports: Some(ports), - open_stdin: Some(true), - stdin_once: Some(false), - tty: Some(true), - env: Some(envars), - volumes: Some(volumes), - cmd: Some(args), - host_config: Some(HostConfig { - binds: Some(vec![]), - network_mode: Some("bridge".to_string()), - port_bindings: Some(port_map), - mounts: Some(mounts), - ..Default::default() - }), - networking_config: Some(NetworkingConfig { - endpoints_config: endpoints, - }), - ..Default::default() - }; - info!("Creating {}", image_name); - debug!("Options: {:?}", options); - debug!("{} has configuration object: {:#?}", image_name, config); - let container = docker.create_container(options, config).await?; - let name = image.container_name(); - let id = container.id.clone(); - self.add_container(name, container); - info!("Starting {}.", image_name); - docker.start_container::(id.as_str(), None).await?; - self.mark_container_running(name)?; - info!("{} started with id {}", image_name, id); - - Ok(name.to_string()) - } - - // helper function for start recipe. This will be overhauled to be more flexible in future - fn images_to_start(&self) -> Vec { - let mut images = Vec::with_capacity(6); - // Always use Tor for now - images.push(ImageType::Tor); - if self.config.base_node.is_some() { - images.push(ImageType::BaseNode); - } - if self.config.wallet.is_some() { - images.push(ImageType::Wallet); - } - if self.config.xmrig.is_some() { - images.push(ImageType::XmRig); - } - if self.config.sha3_miner.is_some() { - images.push(ImageType::Sha3Miner); - } - if self.config.mm_proxy.is_some() { - images.push(ImageType::MmProxy); - } - // TODO - add monerod support - images - } - - /// Returns a reference to the set of managed containers. You can only retrieve immutable references from this - /// hash map. If you need a mutable reference tot a container's state, see [`container_mut`]. - pub fn managed_containers(&self) -> &HashMap { - &self.containers - } - - /// Return a mutable reference to the named container's [`ContainerState`]. - pub fn container_mut(&mut self, container_name: &str) -> Option<&mut ContainerState> { - self.containers.get_mut(container_name) - } - - /// Add the container info to the list of containers the wrapper is managing - fn add_container(&mut self, name: &str, container: ContainerCreateResponse) { - let id = ContainerId::from(container.id.clone()); - let state = ContainerState::new(name.to_string(), id, container); - self.containers.insert(name.to_string(), state); - } - - // Tag the container with id `id` as Running - fn mark_container_running(&mut self, name: &str) -> Result<(), DockerWrapperError> { - if let Some(container) = self.containers.get_mut(name) { - container.running(); - Ok(()) - } else { - Err(DockerWrapperError::ContainerNotFound(name.to_string())) - } - } - - /// Stop the container with the given `name` and optionally delete it - pub async fn stop_container(&mut self, name: &str, delete: bool, docker: &Docker) { - let container = match self.container_mut(name) { - Some(c) => c, - None => { - info!( - "Cannot stop container {}. It was not found in the current workspace", - name - ); - return; - }, - }; - let options = StopContainerOptions { t: 0 }; - let id = container.id().clone(); - match docker.stop_container(id.as_str(), Some(options)).await { - Ok(_res) => { - info!("Container {} stopped", id); - container.set_stop(); - }, - Err(err) => { - warn!("Could not stop container {} due to {}", id, err.to_string()); - }, - } - // Even if stopping failed (maybe it was already stopped), try and delete it - if delete { - match docker.remove_container(id.as_str(), None).await { - Ok(()) => { - info!("Container {} deleted", id); - container.set_deleted(); - }, - Err(err) => { - warn!("Could not delete container {} due to: {}", id, err.to_string()) - }, - } - } - } - - /// Stop all running containers and optionally delete them - pub async fn stop_containers(&mut self, delete: bool, docker: &Docker) { - let names = self.containers.keys().cloned().collect::>(); - for name in names { - // Stop the container immediately - self.stop_container(name.as_str(), delete, docker).await; - } - } - - /// Returns the network name - pub fn network_name(&self) -> String { - format!("{}_network", self.name) - } - - /// Returns the name of the volume holding blockchain data. Currently this is namespaced to the workspace. We might - /// want to change this to be shareable across networks, i.e. only namespace across the network type. - pub fn tari_blockchain_volume_name(&self) -> String { - format!("{}_{}_volume", self.name, self.config.tari_network.lower_case()) - } - - /// Checks if the network for this docker configuration exists - pub async fn network_exists(&self, docker: &Docker) -> Result { - let name = self.network_name(); - let options = InspectNetworkOptions { - verbose: false, - scope: "local", - }; - let network = docker.inspect_network(name.as_str(), Some(options)).await; - // hardcore pattern matching yo! - if let Ok(Network { - name: Some(name), - id: Some(id), - .. - }) = network - { - info!("Network {} (id:{}) exists", name, id); - Ok(true) - } else { - info!("Network {} does not exist", name); - Ok(false) - } - } - - /// Create a network in docker to allow the containers in this workspace to communicate with each other. - pub async fn create_network(&self, docker: &Docker) -> Result<(), DockerWrapperError> { - let name = self.network_name(); - let options = CreateNetworkOptions { - name: name.as_str(), - check_duplicate: true, - driver: "bridge", - internal: false, - attachable: false, - ingress: false, - ipam: Default::default(), - enable_ipv6: false, - options: Default::default(), - labels: Default::default(), - }; - let res = docker.create_network(options).await?; - if let Some(id) = &res.id { - info!("Network {} (id:{}) created", name, id); - } - if let Some(warn) = res.warning { - warn!("Creating {} network had warnings: {}", name, warn); - } - Ok(()) - } - - /// Checks whether the blockchain data volume exists - pub async fn volume_exists(&self, docker: &Docker) -> Result { - let name = self.tari_blockchain_volume_name(); - let volume = docker.inspect_volume(name.as_str()).await?; - trace!("Volume {} exists at {}", name, volume.mountpoint); - Ok(true) - } - - /// Tries to create a new blockchain data volume for this workspace. - pub async fn create_volume(&self, docker: &Docker) -> Result<(), DockerWrapperError> { - let name = self.tari_blockchain_volume_name(); - let config = CreateVolumeOptions { - name, - driver: "local".to_string(), - ..Default::default() - }; - let volume = docker.create_volume(config).await?; - info!("Docker volume {} created at {}", volume.name, volume.mountpoint); - Ok(()) - } - - /// Connects a container to the workspace network. This is not typically needed, since the container will - /// automatically be connected to the network when it is created in [`start_service`]. - pub async fn connect_to_network( - &self, - id: &ContainerId, - image: ImageType, - docker: &Docker, - ) -> Result<(), DockerWrapperError> { - let network = self.network_name(); - let options = ConnectNetworkOptions { - container: id.as_str(), - endpoint_config: EndpointSettings { - aliases: Some(vec![image.container_name().to_string()]), - ..Default::default() - }, - }; - info!( - "Connecting container {} ({}) to network {}...", - image.image_name(), - id, - network - ); - docker.connect_network(network.as_str(), options).await?; - info!( - "Docker container {} ({}) connected to network {}", - image.image_name(), - id, - network - ); - Ok(()) - } -} diff --git a/applications/launchpad/backend/src/docker/wrapper.rs b/applications/launchpad/backend/src/docker/wrapper.rs deleted file mode 100644 index 13de2c22f4..0000000000 --- a/applications/launchpad/backend/src/docker/wrapper.rs +++ /dev/null @@ -1,93 +0,0 @@ -// Copyright 2021. The Tari Project -// -// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the -// following conditions are met: -// -// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following -// disclaimer. -// -// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the -// following disclaimer in the documentation and/or other materials provided with the distribution. -// -// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote -// products derived from this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, -// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR -// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, -// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE -// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -// - -use std::collections::HashMap; - -use bollard::{ - image::CreateImageOptions, - models::{CreateImageInfo, SystemEventsResponse}, - system::EventsOptions, - Docker, -}; -use futures::{Stream, TryStreamExt}; - -use crate::docker::DockerWrapperError; - -/// A wrapper around a [`bollard::Docker`] instance providing some opinionated convenience methods for Tari workspaces. -pub struct DockerWrapper { - handle: Docker, -} - -impl DockerWrapper { - /// Create a new wrapper - pub fn new() -> Result { - let handle = Docker::connect_with_local_defaults()?; - Ok(Self { handle }) - } - - /// Returns the version of the _docker client_. - pub fn version(&self) -> String { - self.handle.client_version().to_string() - } - - /// Create a (cheap) clone of the [`Docker`] instance, suitable for passing to threads and futures. - pub fn handle(&self) -> Docker { - self.handle.clone() - } - - /// Pull an image from a repository. - /// - /// image_name: The fully qualified name of the image, {registry}/{name}:{tag} - /// To use the default registry and tag, you can call `Self::fully_qualified_image(image, registry, tag)` - /// to build a default full-qualified image name. - pub async fn pull_image( - &self, - image_name: String, - ) -> impl Stream> { - let opts = Some(CreateImageOptions { - from_image: image_name, - ..Default::default() - }); - let stream = self.handle.create_image(opts, None, None); - stream.map_err(DockerWrapperError::from) - } - - /// Returns a stream of relevant events. We're opinionated here, so we filter the stream to only return - /// container, image, network and volume events. - pub async fn events(&self) -> impl Stream> { - let docker = self.handle.clone(); - let mut type_filter = HashMap::new(); - type_filter.insert("type".to_string(), vec![ - "container".to_string(), - "image".to_string(), - "network".to_string(), - "volume".to_string(), - ]); - let options = EventsOptions { - since: None, - until: None, - filters: type_filter, - }; - docker.events(Some(options)).map_err(DockerWrapperError::from) - } -} diff --git a/applications/launchpad/backend/src/error.rs b/applications/launchpad/backend/src/error.rs deleted file mode 100644 index 0562992ed0..0000000000 --- a/applications/launchpad/backend/src/error.rs +++ /dev/null @@ -1,58 +0,0 @@ -// Copyright 2021. The Tari Project -// -// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the -// following conditions are met: -// -// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following -// disclaimer. -// -// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the -// following disclaimer in the documentation and/or other materials provided with the distribution. -// -// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote -// products derived from this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, -// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR -// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, -// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE -// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -// - -use std::error::Error; - -use tauri::{api::Error as TauriApiError, Error as TauriError}; -use thiserror::Error; - -use crate::docker::DockerWrapperError; - -#[derive(Debug, Error)] -pub enum LauncherError { - #[error("Something went wrong with the Docker Wrapper")] - DockerWrapperError(#[from] DockerWrapperError), - #[error("Something went wrong on your filesystem")] - FileSystemError(#[from] std::io::Error), - #[error("Something went screwy with Tauri")] - TauriError(#[from] TauriError), - #[error("Something went awry with the Tauri API")] - TauriApiError(#[from] TauriApiError), - #[error("A workspace configuration object is required")] - MissingConfig, - #[error("{1} is required because we are creating a {0}")] - ConfigVariableRequired(String, String), -} - -impl LauncherError { - /// Combine all error messages down the chain into one string. - pub fn chained_message(&self) -> String { - let mut messages = vec![self.to_string()]; - let mut this = self as &dyn Error; - while let Some(next) = this.source() { - messages.push(next.to_string()); - this = next; - } - messages.join(" caused by:\n") - } -} diff --git a/applications/launchpad/backend/src/main.rs b/applications/launchpad/backend/src/main.rs deleted file mode 100644 index 4616bdc7b0..0000000000 --- a/applications/launchpad/backend/src/main.rs +++ /dev/null @@ -1,96 +0,0 @@ -// Copyright 2022 The Tari Project -// SPDX-License-Identifier: BSD-3-Clause - -// FIXME: ignoring for now -#![allow(unused_imports)] -#![allow(dead_code)] -#![cfg_attr(all(not(debug_assertions), target_os = "windows"), windows_subsystem = "windows")] -use log::*; -mod commands; -mod docker; -mod error; -use docker::{DockerWrapper, Workspaces}; -use tauri::{api::cli::get_matches, async_runtime::block_on, utils::config::CliConfig, Manager, PackageInfo, RunEvent}; - -use crate::commands::{ - create_default_workspace, - create_new_workspace, - events, - image_list, - launch_docker, - pull_images, - shutdown, - start_service, - stop_service, - AppState, -}; - -fn main() { - env_logger::init(); - let context = tauri::generate_context!(); - let cli_config = context.config().tauri.cli.clone().unwrap(); - - // We're going to attach this to the AppState because Tauri does not expose it for some reason - let package_info = context.package_info().clone(); - // Handle --help and --version. Exits after printing - handle_cli_options(&cli_config, &package_info); - - let docker = match DockerWrapper::new() { - Ok(docker) => docker, - Err(err) => { - error!("Could not launch docker backend. {}", err.chained_message()); - std::process::exit(-1); - }, - }; - - // TODO - Load workspace definitions from persistent storage here - let workspaces = Workspaces::default(); - info!("Using Docker version: {}", docker.version()); - - tauri::Builder::default() - .manage(AppState::new(docker, workspaces, package_info)) - .invoke_handler(tauri::generate_handler![ - image_list, - pull_images, - create_new_workspace, - create_default_workspace, - events, - launch_docker, - start_service, - stop_service, - shutdown - ]) - .run(context) - .expect("error starting"); - // .build(context) - // .expect("error while running Launchpad"); - - // app.run(|app, event| { - // if let RunEvent::Exit = event { - // info!("Received Exit event"); - // block_on(async move { - // let state = app.state(); - // let _message = shutdown(state).await; - // }); - // } - // }); -} - -fn handle_cli_options(cli_config: &CliConfig, pkg_info: &PackageInfo) { - match get_matches(cli_config, pkg_info) { - Ok(matches) => { - if let Some(arg_data) = matches.args.get("help") { - debug!("{}", arg_data.value.as_str().unwrap_or("No help available")); - std::process::exit(0); - } - if let Some(arg_data) = matches.args.get("version") { - debug!("{}", arg_data.value.as_str().unwrap_or("No version data available")); - std::process::exit(0); - } - }, - Err(e) => { - error!("{}", e.to_string()); - std::process::exit(1); - }, - } -} diff --git a/applications/launchpad/backend/tauri.conf.json b/applications/launchpad/backend/tauri.conf.json deleted file mode 100644 index fb281abbed..0000000000 --- a/applications/launchpad/backend/tauri.conf.json +++ /dev/null @@ -1,78 +0,0 @@ -{ - "package": { - "productName": "tari-launchpad", - "version": "1.1.0" - }, - "build": { - "distDir": "../gui-vue/dist", - "devPath": "http://localhost:8080", - "beforeDevCommand": " cd ../gui-vue && npm run serve", - "beforeBuildCommand": "cd ../gui-vue && npm run build" - }, - "tauri": { - "cli": { - "description": "A simple single-click UI to launch a Tari node, wallet and miner", - "args": [ - { - "short": "c", - "name": "config-file", - "description": "The path to the configuration file to use for the launcher", - "takesValue": true - } - ], - "subcommands": {} - }, - "bundle": { - "active": true, - "targets": "all", - "identifier": "com.tari.launchpad", - "icon": [ - "icons/favicon-32x32.png", - "icons/apple-icon-120x120.png", - "icons/android-icon-192x192.png" - ], - "resources": ["assets/*"], - "externalBin": [], - "copyright": "(c) The Tari Development community 2021", - "category": "DeveloperTool", - "shortDescription": "", - "longDescription": "", - "deb": { - "depends": [] - }, - "macOS": { - "frameworks": [], - "minimumSystemVersion": "", - "exceptionDomain": "", - "signingIdentity": null, - "entitlements": null - }, - "windows": { - "certificateThumbprint": null, - "digestAlgorithm": "sha256", - "timestampUrl": "" - } - }, - "updater": { - "active": false - }, - "allowlist": { - "dialog": { - "open": true - }, - "all": true - }, - "windows": [ - { - "title": "Tari Launchpad", - "width": 1600, - "height": 800, - "resizable": true, - "fullscreen": false - } - ], - "security": { - "csp": "default-src blob: data: filesystem: ws: wss: http: https: tauri: 'unsafe-eval' 'unsafe-inline' 'self' img-src: 'self'" - } - } -} \ No newline at end of file diff --git a/applications/launchpad/build_images.sh b/applications/launchpad/build_images.sh deleted file mode 100755 index 2950662092..0000000000 --- a/applications/launchpad/build_images.sh +++ /dev/null @@ -1,29 +0,0 @@ -#!/bin/bash -source versions.txt -platform=${BUILD_PLATFORM:-amd64} - -build_image() { - echo "Building $1 image v$VERSION.." - docker build -f docker_rig/$1 --build-arg ARCH=native --build-arg FEATURES=avx2 --build-arg VERSION=$VERSION $3 $4 -t quay.io/tarilabs/$2:latest-$platform ./../.. - docker tag quay.io/tarilabs/$2:latest-$platform quay.io/tarilabs/$2:$VERSION-$platform - docker push quay.io/tarilabs/$2:latest-$platform - docker push quay.io/tarilabs/$2:$VERSION -} - -build_image base_node.Dockerfile tari_base_node -build_image console_wallet.Dockerfile tari_console_wallet -build_image mm_proxy.Dockerfile tari_mm_proxy -build_image sha3_miner.Dockerfile tari_sha3_miner -build_image tor.Dockerfile tor -build_image monerod.Dockerfile monerod - -echo "Building XMRig image v$VERSION (XMRig v$XMRIG_VERSION)" -docker build -f docker_rig/xmrig.Dockerfile --build-arg VERSION=$VERSION --build-arg XMRIG_VERSION=$XMRIG_VERSION -t quay.io/tarilabs/xmrig:latest-$platform ./../.. -docker tag quay.io/tarilabs/xmrig:latest-$platform quay.io/tarilabs/xmrig:$VERSION-$platform -docker push quay.io/tarilabs/xmrig:latest-$platform -docker push quay.io/tarilabs/xmrig:$VERSION - -docker build -f docker_rig/frontail.Dockerfile -t quay.io/tarilabs/frontail:latest-$platform ./docker_rig -docker tag quay.io/tarilabs/frontail:latest-$platform quay.io/tarilabs/frontail:$VERSION-$platform -docker push quay.io/tarilabs/frontail:latest-$platform -docker push quay.io/tarilabs/frontail:$VERSION diff --git a/applications/launchpad/docker_rig/README.md b/applications/launchpad/docker_rig/README.md deleted file mode 100644 index 958f13f119..0000000000 --- a/applications/launchpad/docker_rig/README.md +++ /dev/null @@ -1,217 +0,0 @@ -# Tari Launchpad - CLI edition - -a.k.a. _Tari one-click miner_. - -Currently this only works on MacOS and Linux. -Contributions for `quick_start.bat` are welcomed. - -## Prerequisites - -1. [Docker](https://docs.docker.com/get-docker/) -2. Set up some environment variables: - - `DATA_FOLDER` (required). The path to store the Tari configuration files and logs. You can create multiple - network setups, each in their own sandbox. - - `TARI_NETWORK` (optional). Default=dibbler. Specify the Tari network to connect to. - - `START_TOR` (optional). Default=1. Whether we should start a Tor instance - - `START_BASE_NODE` (optional). Default=1. Whether we should start a base node instance - - `START_WALLET` (optional). Default=1. Whether we should start a wallet instance (GRPC only) - - `START_MINER` (optional). Default=1. Whether we should fire up a SHA_3 solo miner. - - `USE_OWN_MODEROD` (optional). Default=0. Whether we should start and sync our own Monero node. - - `START_MONERO_MM` (optional). Default=1. Whether we should start merge-mining on Monero. - - - `TARI_WALLET_PASSWORD` (optional). Highly recommended. Default: tari. The password for your Tari wallet. - - `TARI_WALLET__DIBBLER__TOR_CONTROL_AUTH` (optional). Default: tari. The password for the Tor control auth. - - `TARI_MONERO_WALLET_ADDRESS` (optional). Though you'll donate funds to some random address if you don't set it. - -## Quick Start - -`./quick-start.sh` - -And you should be golden. - -## Interacting with the wallet and base node. - -Any Tari client that supports gRPC can be connected to the node setup. With the default configuration, the base node -will be at `http://127.0.0.1:18142` and the wallet will be at `http://127.0.0.1:18143`. - -[Kreya](https://kreya.app/) is a fairly user-friendly gRPC client that can give you a rudimentary UI to the docker rig -out of the box: - -#### Getting block information - -![get_blocks](img/node_blocks.jpg) - -#### Wallet - whoami - -![whoami](img/wallet_id.jpg) - -#### Wallet - send transactions - -![send](img/wallet_send.jpg) - -## Viewing logs and configuration files - -Logs and configuration files are stored in `{DATA_FOLDER}/{app}/log/*.log`. - -So assuming your data folder is `/tmp/tari1/, the generated file structure looks like: - -```text -/tmp/tari1$ ll -R -drwxr-xr-x user user 128 B Wed Nov 3 15:56:53 2021  base_node/ -drwxr-xr-x user user 96 B Wed Nov 3 15:56:53 2021  config/ -drwxr-xr-x user user 128 B Mon Nov 1 12:55:50 2021  mm_proxy/ -drwxr-xr-x user user 128 B Mon Nov 1 12:55:58 2021  sha3_miner/ -drwxr-xr-x user user 64 B Wed Nov 3 15:56:53 2021  tor/ -drwxr-xr-x user user 192 B Mon Nov 1 12:56:17 2021  wallet/ -drwxr-xr-x user user 96 B Mon Nov 1 12:20:31 2021  xmrig/ - -./base_node: -drwxr-xr-x user user 128 B Fri Oct 29 17:44:56 2021  config/ -drwxr-xr-x user user 320 B Wed Nov 3 15:40:55 2021  log/ - -./base_node/config: -.rw-r--r-- user user 312 B Mon Nov 1 20:09:38 2021  base_node_id.json -.rw-r--r-- user user 211 B Mon Nov 1 20:09:38 2021  base_node_tor.json - -./base_node/log: -.rw-r--r-- user user 3.3 MB Wed Nov 3 15:54:20 2021  core.log -.rw-r--r-- user user 1 MB Wed Nov 3 15:53:10 2021  network.log -.rw-r--r-- user user 0 B Mon Nov 1 13:01:33 2021  other.log - -./config: -.rw-r--r-- user user 4.1 KB Mon Nov 1 20:12:10 2021  log4rs.yml - -./mm_proxy: -drwxr-xr-x user user 128 B Mon Nov 1 12:53:33 2021  config/ -drwxr-xr-x user user 352 B Wed Nov 3 14:10:04 2021  log/ - -./mm_proxy/config: -.rw-r--r-- user user 8.7 KB Mon Nov 1 12:53:33 2021  config.toml -.rw-r--r-- user user 1.1 KB Mon Nov 1 12:53:33 2021  log4rs.yml - -./mm_proxy/log: -.rw-r--r-- user user 8.4 MB Wed Nov 3 15:53:53 2021  core.log -.rw-r--r-- user user 0 B Mon Nov 1 13:01:38 2021  network.log -.rw-r--r-- user user 0 B Mon Nov 1 13:01:38 2021  other.log - -./sha3_miner: -drwxr-xr-x user user 192 B Mon Nov 1 13:01:36 2021  log/ - -./sha3_miner/log: -.rw-r--r-- user user 677.7 KB Wed Nov 3 15:49:31 2021  core.log -.rw-r--r-- user user 0 B Mon Nov 1 13:01:36 2021  network.log -.rw-r--r-- user user 0 B Mon Nov 1 13:01:36 2021  other.log - -./wallet: -drwxr-xr-x user user 192 B Mon Nov 1 12:56:17 2021  ./ -drwxr-xr-x user user 288 B Wed Nov 3 15:56:53 2021  ../ -drwxr-xr-x user user 256 B Wed Nov 3 15:23:19 2021  log/ -drwxr-xr-x user user 128 B Wed Nov 3 15:54:10 2021  dibbler/ - -./wallet/log: -.rw-r--r-- user user 564.2 KB Wed Nov 3 15:54:20 2021  core.log -.rw-r--r-- user user 280.3 KB Wed Nov 3 15:54:08 2021  network.log -.rw-r--r-- user user 0 B Mon Nov 1 13:01:24 2021  other.log - -./xmrig: -drwxr-xr-x user user 96 B Mon Nov 1 12:20:31 2021  ./ -drwxr-xr-x user user 288 B Wed Nov 3 15:56:53 2021  ../ -.rw-r--r-- user user 212.9 KB Wed Nov 3 15:53:53 2021  xmrig.log -``` - -You can edit the network configuration (requires a container restart) and log configuration (takes effect within 30s) -by editing the files in the top-level `config` folder. - - - -## Current shortcomings and TODOs - -* It's a CLI only. Next step is to build a nice UI wrapper around this. - -* TODO - Windows support. - -* The blockchain data is stores in docker volumes, and not on the host machine directly. This is due to crippling performance -limitations one suffers when mounting host file system from Windows or MacOS into docker containers. -This isn't a big drawback, since you seldom want or need to access the raw blockchain database files anyway. Are they're -[still accessible](#accessing-blockchain-data). But **ensure that you reserve enough space to store the Tari, and optionally, -Monero blockchains inside the Docker VM**. - -* TODO - Multi-network sandboxing. Sandboxing currently doesn't work across different networks (e.g. dibbler and mainnet). This should be fixed soon. - -* Local MoneroD support. The merge-miner proxy doesn't actually use the local MoneroD container if it is running. This - is left as a TODO for the UI version. - -* Tor control password. For now, you set this manually. In the UI edition, this will be ephemeral and configured automatically. - -* GRPC addresses. The code only supports IPv4 addresses as gRPC configuration parameters. This makes the docker networking - fragile since the IP addresses must be guessed. TODO - accept DNS names in gRPC configs to make this more robust. - - - - -### Accessing blockchain data - -The blockchain data is stored in a docker volume for performance reasons. If you need to back up or access the LMDB -a blockchain data, you can use something like this to extract it to the host filesystem: - -`docker run --rm -v $(pwd):/backup -v blockchain:/blockchain ubuntu tar czvf /backup/backup.tar.gz /blockchain` - - -## Layout - - +-----------------------+ - | | -+---->| Console Wallet +------------------+ -| | | | -| +----------+------------+ | -| | | -| | gRPC | -| | | -| | | -| +----------v------------+ +------v-----+ -| | | Socks5 | | -| | Base Node +---------->| Tor |----> Network -| | | | | -| +----------^------------+ +------------+ -| | -| | -| | -| +----------+------------+ -| | | -+-----+ SHA3-Miner | -| | | -| +-----------------------+ -| -| -| -| +-----------------------+ -| | | -+-----+ XMRRig etc | - | | - +-----------------------+ - -#### Notes - -Building docker images: - -``` -cd buildtools/docker_rig -docker build -t quay.io/tarilabs/tor:latest -f base_node.Dockerfile . -docker build -t quay.io/tarilabs/tari_base_node:latest -f base_node.Dockerfile ../../ -``` - -Base node/Wallet config for using the Tor docker container: - -```toml -tcp_listener_address = "/ip4/0.0.0.0/tcp/18189" -transport = "tor" -tor_control_address = "/dns4/tor/tcp/9051" -tor_control_auth = "password=asdf" # replace with your configured password -tor_onion_port = 18141 -tor_forward_address = "/ip4/0.0.0.0/tcp/18189" -tor_socks_address_override="/dns4/tor/tcp/9050" -``` - -When attaching to a running container: - -To detach the tty without exiting the shell/program, use the escape sequence ^P^Q (Ctrl+P followed by Ctrl+Q). diff --git a/applications/launchpad/docker_rig/base_node.Dockerfile b/applications/launchpad/docker_rig/base_node.Dockerfile deleted file mode 100644 index 42605a48fa..0000000000 --- a/applications/launchpad/docker_rig/base_node.Dockerfile +++ /dev/null @@ -1,92 +0,0 @@ -FROM rust:1.60-buster as builder - -ARG DEBIAN_FRONTEND=noninteractive -RUN apt update && apt -y install \ - apt-transport-https \ - bash \ - ca-certificates \ - curl \ - gpg \ - iputils-ping \ - less \ - libreadline-dev \ - libsqlite3-0 \ - openssl \ - telnet \ - cargo \ - clang \ - cmake - -RUN apt update && apt upgrade -y && apt clean - - -WORKDIR /tari - -# Adding only necessary things up front and copying the entrypoint script last -# to take advantage of layer caching in docker -ADD Cargo.lock applications/deps_only/Cargo.lock -ADD rust-toolchain.toml . - -ARG ARCH=native -ARG FEATURES=avx2 -ENV RUSTFLAGS="-C target_cpu=$ARCH" -ENV ROARING_ARCH=$ARCH -ENV CARGO_HTTP_MULTIPLEXING=false - -# Caches downloads across docker builds -ADD applications/deps_only applications/deps_only -WORKDIR applications/deps_only -RUN cargo build --bin deps_only --release - -WORKDIR /tari - -ADD Cargo.toml . -ADD Cargo.lock . -ADD applications applications -ADD base_layer base_layer -ADD clients clients -ADD common common -ADD common_sqlite common_sqlite -ADD comms comms -ADD infrastructure infrastructure -ADD dan_layer dan_layer -ADD meta meta - -RUN cargo build --bin tari_base_node --release --features $FEATURES --locked - -# Create a base minimal image for the executables -FROM bitnami/minideb:bullseye as base -ARG VERSION=1.0.1 -# Disable Prompt During Packages Installation -ARG DEBIAN_FRONTEND=noninteractive -RUN install_packages \ - apt-transport-https \ - bash \ - ca-certificates \ - curl \ - gpg \ - iputils-ping \ - less \ - libreadline8 \ - libreadline-dev \ - libsqlite3-0 \ - openssl \ - telnet - -RUN groupadd -g 1000 tari && useradd -s /bin/bash -u 1000 -g 1000 tari - -RUN mkdir -p "/var/tari/base_node/dibbler" \ - && mkdir -p "/var/tari/base_node/igor" \ - && mkdir -p "/var/tari/base_node/mainnet" \ - && chown -R tari.tari "/var/tari/base_node" - -RUN mkdir /blockchain && chown tari.tari /blockchain && chmod 777 /blockchain -USER tari -ENV dockerfile_version=$VERSION -ENV APP_NAME=base_node APP_EXEC=tari_base_node - -COPY --from=builder /tari/target/release/$APP_EXEC /usr/bin/ -COPY /applications/launchpad/docker_rig/start_tari_app.sh /usr/bin/start_tari_app.sh - - -ENTRYPOINT [ "start_tari_app.sh", "-c", "/var/tari/config/config.toml", "-b", "/var/tari/base_node" ] diff --git a/applications/launchpad/docker_rig/config.toml b/applications/launchpad/docker_rig/config.toml deleted file mode 100644 index 982b94fc30..0000000000 --- a/applications/launchpad/docker_rig/config.toml +++ /dev/null @@ -1,83 +0,0 @@ -[common] - -[dibbler.p2p.seeds] -dns_seeds = ["seeds.dibbler.tari.com"] -peer_seeds = [ - # 333388d1cbe3e2bd17453d052f - "c2eca9cf32261a1343e21ed718e79f25bfc74386e9305350b06f62047f519347::/onion3/6yxqk2ybo43u73ukfhyc42qn25echn4zegjpod2ccxzr2jd5atipwzqd:18141", - # 555575715a49fc242d756e52ca - "42fcde82b44af1de95a505d858cb31a422c56c4ac4747fbf3da47d648d4fc346::/onion3/2l3e7ysmihc23zybapdrsbcfg6omtjtfkvwj65dstnfxkwtai2fawtyd:18141", - # 77771f53be07fab4be5f1e1ff7 - "50e6aa8f6c50f1b9d9b3d438dfd2a29cfe1f3e3a650bd9e6b1e10f96b6c38f4d::/onion3/7s6y3cz5bnewlj5ypm7sekhgvqjyrq4bpaj5dyvvo7vxydj7hsmyf5ad:18141", - # 9999016f1f3a6162dddf5a45aa - "36a9df45e1423b5315ffa7a91521924210c8e1d1537ad0968450f20f21e5200d::/onion3/v24qfheti2rztlwzgk6v4kdbes3ra7mo3i2fobacqkbfrk656e3uvnid:18141", - # bbbb8358387d81c388fadb4649 - "be128d570e8ec7b15c101ee1a56d6c56dd7d109199f0bd02f182b71142b8675f::/onion3/ha422qsy743ayblgolui5pg226u42wfcklhc5p7nbhiytlsp4ir2syqd:18141", - # eeeeb0a943ed143e613a135392 - "3e0321c0928ca559ab3c0a396272dfaea705efce88440611a38ff3898b097217::/onion3/sl5ledjoaisst6d4fh7kde746dwweuge4m4mf5nkzdhmy57uwgtb7qqd:18141", - # 66664a0f95ce468941bb9de228 - "b0f797e7413b39b6646fa370e8394d3993ead124b8ba24325c3c07a05e980e7e::/ip4/35.177.93.69/tcp/18189", - # 22221bf814d5e524fce9ba5787 - "0eefb45a4de9484eca74846a4f47d2c8d38e76be1fec63b0112bd00d297c0928::/ip4/13.40.98.39/tcp/18189", - # 4444a0efd8388739d563bdd979 - "544ed2baed414307e119d12894e27f9ddbdfa2fd5b6528dc843f27903e951c30::/ip4/13.40.189.176/tcp/18189" -] - - -[base_node] -network = "dibbler" -grpc_address = "/ip4/0.0.0.0/tcp/18142" - -[base_node.storage] -track_reorgs = true - -[dibbler.base_node] -data_dir = "/blockchain/dibbler" -#identity_file = "/var/tari/base_node/config/dibbler/tari_base_node_id.json" - -[igor.base_node] -network = "igor" -data_dir = "/blockchain/igor" - -[base_node.p2p.transport] -tor.control_auth = "password=tari" -#tcp.listener_address = "/dns4/base_node/tcp/18189" -tor.socks_address_override = "/dns4/tor/tcp/9050" -tor.control_address = "/dns4/tor/tcp/9051" - - -[wallet] -override_from = "dibbler" -grpc_address = "/ip4/0.0.0.0/tcp/18143" -password = "tari" - -[dibbler.wallet] -network = "dibbler" -db_file = "wallet/dibbler/wallet.dat" - -[igor.wallet] -network = "igor" -db_file = "wallet/igor/wallet.dat" - -[miner] -base_node_grpc_address = "/dns4/base_node/tcp/18142" -wallet_grpc_address = "/dns4/wallet/tcp/18143" -mine_on_tip_only = true - -[merge_mining_proxy] -#config = "dibbler" -monerod_url = [ # stagenet - "http://stagenet.xmr-tw.org:38081", - "http://stagenet.community.xmr.to:38081", - "http://monero-stagenet.exan.tech:38081", - "http://xmr-lux.boldsuck.org:38081", - "http://singapore.node.xmr.pm:38081", -] -base_node_grpc_address = "/dns4/base_node/tcp/18142" -console_wallet_grpc_address = "/dns4/wallet/tcp/18143" -listener_address = "/dns4/mm_proxy/tcp/18081" -submit_to_origin = true -monerod_username = "" -monerod_password = "" -monerod_use_auth = false - diff --git a/applications/launchpad/docker_rig/console_wallet.Dockerfile b/applications/launchpad/docker_rig/console_wallet.Dockerfile deleted file mode 100644 index 9fb34e28ed..0000000000 --- a/applications/launchpad/docker_rig/console_wallet.Dockerfile +++ /dev/null @@ -1,84 +0,0 @@ -FROM rust:1.60-buster as builder - -ARG DEBIAN_FRONTEND=noninteractive -RUN apt update && apt -y install \ - apt-transport-https \ - bash \ - ca-certificates \ - curl \ - gpg \ - iputils-ping \ - less \ - libreadline-dev \ - libsqlite3-0 \ - openssl \ - telnet \ - cargo \ - clang \ - cmake - -RUN apt update && apt upgrade -y && apt clean - - -WORKDIR /tari - -# Adding only necessary things up front and copying the entrypoint script last -# to take advantage of layer caching in docker -ADD Cargo.lock applications/deps_only/Cargo.lock -ADD rust-toolchain.toml . - -ARG ARCH=native -ARG FEATURES=avx2 -ENV RUSTFLAGS="-C target_cpu=$ARCH" -ENV ROARING_ARCH=$ARCH -ENV CARGO_HTTP_MULTIPLEXING=false - -# Caches downloads across docker builds -ADD applications/deps_only applications/deps_only -WORKDIR applications/deps_only -RUN cargo build --bin deps_only --release - -WORKDIR /tari - -ADD Cargo.toml . -ADD Cargo.lock . -ADD applications applications -ADD base_layer base_layer -ADD clients clients -ADD common common -ADD common_sqlite common_sqlite -ADD comms comms -ADD infrastructure infrastructure -ADD dan_layer dan_layer -ADD meta meta - -RUN cargo build --bin tari_console_wallet --release --features $FEATURES --locked - -# Create a base minimal image for the executables -FROM bitnami/minideb:bullseye as base -ARG VERSION=1.0.1 -# Disable Prompt During Packages Installation -ARG DEBIAN_FRONTEND=noninteractive -RUN apt update && apt -y install \ - apt-transport-https \ - bash \ - ca-certificates \ - curl \ - gpg \ - iputils-ping \ - less \ - libreadline8 \ - libreadline-dev \ - libsqlite3-0 \ - openssl \ - telnet - -RUN groupadd -g 1000 tari && useradd -s /bin/bash -u 1000 -g 1000 tari -USER tari -ENV dockerfile_version=$VERSION -ENV APP_NAME=wallet APP_EXEC=tari_console_wallet - -COPY --from=builder /tari/target/release/$APP_EXEC /usr/bin/ -COPY applications/launchpad/docker_rig/start_tari_app.sh /usr/bin/start_tari_app.sh - -ENTRYPOINT [ "start_tari_app.sh", "-c", "/var/tari/config/config.toml", "-b", "/var/tari/wallet" ] diff --git a/applications/launchpad/docker_rig/docker-compose.yml b/applications/launchpad/docker_rig/docker-compose.yml deleted file mode 100644 index c3ec02c9c0..0000000000 --- a/applications/launchpad/docker_rig/docker-compose.yml +++ /dev/null @@ -1,168 +0,0 @@ -version: "3.9" -services: - tor: - image: quay.io/tarilabs/tor:latest - build: - context: . - dockerfile: tor.Dockerfile - volumes: - - ${DATA_FOLDER}/tor:/etc/tor/ - - base_node: - image: quay.io/tarilabs/tari_base_node:latest - build: - context: ./../../.. - dockerfile: applications/launchpad/docker_rig/base_node.Dockerfile - args: - ARCH: native - FEATURES: avx2 - environment: - TARI_LOG_CONFIGURATION: "/var/tari/config/log4rs.yml" - APP_NAME: base_node - APP_EXEC: tari_base_node - WAIT_FOR_TOR: ${WAIT_FOR_TOR:-0} - TARI_NETWORK: ${TARI_NETWORK} - TARI_DIBBLER__BASE_NODE__DATA_DIR: "/blockchain/dibbler" - ports: - - 18189:18189 - - 18142:18142 - command: ["--non-interactive"] - depends_on: - - tor - volumes: - - ${DATA_FOLDER}:/var/tari/ - - blockchain:/blockchain/${TARI_NETWORK} - stdin_open: true - tty: true - - wallet: - image: quay.io/tarilabs/tari_console_wallet:latest - build: - context: ./../../.. - dockerfile: applications/launchpad/docker_rig/console_wallet.Dockerfile - args: - ARCH: native - FEATURES: avx2 - environment: - TARI_LOG_CONFIGURATION: "/var/tari/config/log4rs.yml" - APP_NAME: wallet - APP_EXEC: tari_console_wallet - WAIT_FOR_TOR: ${WAIT_FOR_TOR:-0} - SHELL: "/bin/bash" - TERM: "linux" - TARI_WALLET_PASSWORD: ${TARI_WALLET_PASSWORD:-tari} - TARI_NETWORK: ${TARI_NETWORK:-dibbler} - command: ["--non-interactive"] - ports: - - 18188:18188 - - 18143:18143 - depends_on: - - tor - volumes: - - ${DATA_FOLDER}:/var/tari/ - #stdin_open: true - #tty: true - - sha3_miner: - image: quay.io/tarilabs/tari_sha3_miner:latest - build: - context: ./../../.. - dockerfile: applications/launchpad/docker_rig/sha3_miner.Dockerfile - args: - ARCH: native - FEATURES: avx2 - environment: - TARI_LOG_CONFIGURATION: "/var/tari/config/log4rs.yml" - APP_NAME: sha3_miner - APP_EXEC: tari_miner - WAIT_FOR_TOR: 0 - TARI_NETWORK: ${TARI_NETWORK} - TARI_MINER__NUM_MINING_THREADS: 2 - TARI_MINER__MINE_ON_TIP_ONLY: 1 - TARI_MINER__BASE_NODE_ADDR: "/dns4/base_node/tcp/18142" - TARI_MINER__WALLET_ADDR: "/dns4/wallet/tcp/18143" - command: [] - depends_on: - - base_node - - wallet - volumes: - - ${DATA_FOLDER}:/var/tari/ - - xmrig: - image: quay.io/tarilabs/xmrig:latest - build: - context: . - dockerfile: xmrig.Dockerfile - #command: ["--url", "mm_proxy:18081", "--user", "${TARI_MONERO_WALLET_ADDRESS:-859CW1aiA8gUmAaTipKsYrF5r83MesesSjWoJSSRL6nnfi5LqBssxJmg7BzhgXYcjcPARM7bBvFR9H5dJdi6w93eKA53v8G}", "--coin", "monero", "--daemon", "--log-file=/var/xmrig/xmrig.log"] - command: - [ - "--url", - "mm_proxy:18081", - "--user", - "${TARI_MONERO_WALLET_ADDRESS:-5AJ8FwQge4UjT9Gbj4zn7yYcnpVQzzkqr636pKto59jQcu85CFsuYVeFgbhUdRpiPjUCkA4sQtWApUzCyTMmSigFG2hDo48}", - "--coin", - "monero", - "--daemon", - "--log-file=/var/xmrig/xmrig.log", - "--verbose", - ] - depends_on: - - mm_proxy - volumes: - - ${DATA_FOLDER}/xmrig:/var/xmrig/ - - monerod: - image: quay.io/tarilabs/monerod:latest - build: - context: . - dockerfile: monerod.Dockerfile - volumes: - - monero-blockchain:/home/monero/.bitmonero - - ${DATA_FOLDER}/monerod:/home/monerod - command: - - "--non-interactive" - - "--restricted-rpc" - - "--rpc-bind-ip=0.0.0.0" - - "--confirm-external-bind" - - "--enable-dns-blocklist" - - "--log-file=/home/monerod/monerod.log" - - "--fast-block-sync=1" - - "--prune-blockchain" - - "--${MONERO_NETWORK:-mainnet}" - - mm_proxy: - image: quay.io/tarilabs/tari_mm_proxy:latest - build: - context: ./../../.. - dockerfile: applications/launchpad/docker_rig/mm_proxy.Dockerfile - args: - ARCH: native - FEATURES: avx2 - environment: - RUST_LOG: debug - TARI_LOG_CONFIGURATION: "/var/tari/config/log4rs.yml" - APP_NAME: mm_proxy - APP_EXEC: tari_merge_mining_proxy - WAIT_FOR_TOR: 0 - TARI_NETWORK: ${TARI_NETWORK} - TARI_MERGE_MINING_PROXY__BASE_NODE_GRPC_ADDRESS: "/dns4/base_node/tcp/18142" - TARI_MERGE_MINING_PROXY__CONSOLE_WALLET_GRPC_ADDRESS: "/dns4/wallet/tcp/18143" - TARI_MERGE_MINING_PROXY__MONEROD_USERNAME: ${TARI_MONEROD_USERNAME} - TARI_MERGE_MINING_PROXY__MONEROD_PASSWORD: ${TARI_MONEROD_PASSWORD} - TARI_MERGE_MINING_PROXY__MONEROD_USE_AUTH: ${TARI_MONEROD_USE_AUTH:-0} - TARI_MERGE_MINING_PROXY__LISTENER_ADDRESS: "/ip4/0.0.0.0/tcp/18081" - depends_on: - - base_node - - wallet - command: [] - volumes: - - ${DATA_FOLDER}:/var/tari/ -# pool-worker: -# pool-operator: - -volumes: - # The blockchain data is stored in a docker volume for performance reasons. If you need to back up or access the LMDB - # blockchain data, you can use something like - # `docker run --rm -v $(pwd):/backup -v blockchain:/blockchain ubuntu tar czvf /backup/backup.tar.gz /blockchain` - blockchain: - monero-blockchain: diff --git a/applications/launchpad/docker_rig/frontail.Dockerfile b/applications/launchpad/docker_rig/frontail.Dockerfile deleted file mode 100644 index d14d773aa3..0000000000 --- a/applications/launchpad/docker_rig/frontail.Dockerfile +++ /dev/null @@ -1,19 +0,0 @@ -# Package build -FROM node:16-alpine - -ARG BUILDPLATFORM -ARG FRONTAIL_VERSION=4.9.2 -ARG VERSION=1.0.1 - -RUN npm install -g frontail - -ADD run_frontail.sh /usr/bin/ -WORKDIR /var/tari - -EXPOSE 9001 -ENV dockerfile_version=$VERSION -ENV dockerfile_build_arch=$BUILDPLATFORM -ENV frontail_version=$FRONTAIL_VERSION - -ENTRYPOINT ["/usr/bin/run_frontail.sh"] -CMD ["--help"] diff --git a/applications/launchpad/docker_rig/img/node_blocks.jpg b/applications/launchpad/docker_rig/img/node_blocks.jpg deleted file mode 100644 index bf481b296c..0000000000 Binary files a/applications/launchpad/docker_rig/img/node_blocks.jpg and /dev/null differ diff --git a/applications/launchpad/docker_rig/img/wallet_id.jpg b/applications/launchpad/docker_rig/img/wallet_id.jpg deleted file mode 100644 index 2d7a4d6f2e..0000000000 Binary files a/applications/launchpad/docker_rig/img/wallet_id.jpg and /dev/null differ diff --git a/applications/launchpad/docker_rig/img/wallet_send.jpg b/applications/launchpad/docker_rig/img/wallet_send.jpg deleted file mode 100644 index cb832f667b..0000000000 Binary files a/applications/launchpad/docker_rig/img/wallet_send.jpg and /dev/null differ diff --git a/applications/launchpad/docker_rig/log4rs.yml b/applications/launchpad/docker_rig/log4rs.yml deleted file mode 100644 index 719b3ac32c..0000000000 --- a/applications/launchpad/docker_rig/log4rs.yml +++ /dev/null @@ -1,156 +0,0 @@ -# A customised logfile configuration for running the Tari system in docker - -# timestamp [target] LEVEL message -refresh_rate: 30 seconds -appenders: - # An appender named "stdout" that writes to stdout - stdout: - kind: console - encoder: - pattern: "{d(%Y-%m-%d %H:%M:%S.%f)} [{t}] {h({l}):5} {m}{n}" - - # An appender named "network" that writes to a file with a custom pattern encoder - network: - kind: rolling_file - path: "log/network.log" - policy: - kind: compound - trigger: - kind: size - limit: 10mb - roller: - kind: fixed_window - base: 1 - count: 5 - pattern: "log/network.{}.log" - encoder: - pattern: "{d(%Y-%m-%d %H:%M:%S.%f)} [{t}] {l:5} {m}{n}" - - # An appender named "core" that writes to a file with a custom pattern encoder - core: - kind: rolling_file - path: "log/core.log" - policy: - kind: compound - trigger: - kind: size - limit: 10mb - roller: - kind: fixed_window - base: 1 - count: 5 - pattern: "log/core.{}.log" - encoder: - pattern: "{d(%Y-%m-%d %H:%M:%S.%f)} [{t}] {l:5} {m}{n}" - - # An appender named "other" that writes to a file with a custom pattern encoder - other: - kind: rolling_file - path: "log/other.log" - policy: - kind: compound - trigger: - kind: size - limit: 10mb - roller: - kind: fixed_window - base: 1 - count: 5 - pattern: "log/other.{}.log" - encoder: - pattern: "{d(%Y-%m-%d %H:%M:%S.%f)} [{t}] {l:5} {m}{n}" - -# Set the default logging level to "info" -root: - level: info - appenders: - - core - - stdout - -loggers: - # All events that get logged to core. Many of these are in separate log folders corresponding to their docker containers - tari::application: - level: debug - appenders: - - core - additive: false - base_node::app: - level: debug - appenders: - - core - additive: false - c: - level: debug - appenders: - - core - additive: false - tari: - level: debug - appenders: - - core - additive: false - wallet: - level: debug - appenders: - - core - additive: false - tari_miner: - level: debug - appenders: - - core - additive: false - tari_mm_proxy: - level: info - appenders: - - core - additive: false - - tracing: - level: info - appenders: - - other - additive: false - - # Comms messages get logged to the 'network' log - comms: - level: info - appenders: - - network - additive: false - tari_comms: - level: info - appenders: - - network - additive: false - p2p: - level: info - appenders: - - network - additive: false - yamux: - level: info - appenders: - - network - additive: false - mio: - level: error - appenders: - - network - additive: false - - # Miscellaneous events. Log these in 'other' - rustyline: - level: error - appenders: - - other - additive: false - tokio_util: - level: error - appenders: - - other - additive: false - pgp: - level: warn - appenders: - - other - additive: false diff --git a/applications/launchpad/docker_rig/mm_proxy.Dockerfile b/applications/launchpad/docker_rig/mm_proxy.Dockerfile deleted file mode 100644 index 671a5f79b9..0000000000 --- a/applications/launchpad/docker_rig/mm_proxy.Dockerfile +++ /dev/null @@ -1,86 +0,0 @@ -FROM rust:1.60-buster as builder - -ARG DEBIAN_FRONTEND=noninteractive -RUN apt update && apt -y install \ - apt-transport-https \ - bash \ - ca-certificates \ - curl \ - gpg \ - iputils-ping \ - less \ - libreadline-dev \ - libsqlite3-0 \ - openssl \ - telnet \ - cargo \ - clang \ - cmake - -RUN apt update && apt upgrade -y && apt clean - - -WORKDIR /tari - -# Adding only necessary things up front and copying the entrypoint script last -# to take advantage of layer caching in docker -ADD Cargo.lock applications/deps_only/Cargo.lock -ADD rust-toolchain.toml . - -ARG ARCH=native -ARG FEATURES=avx2 -ENV RUSTFLAGS="-C target_cpu=$ARCH" -ENV ROARING_ARCH=$ARCH -ENV CARGO_HTTP_MULTIPLEXING=false - -# Caches downloads across docker builds -ADD applications/deps_only applications/deps_only -WORKDIR applications/deps_only -RUN cargo build --bin deps_only --release - -WORKDIR /tari - -ADD Cargo.toml . -ADD Cargo.lock . -ADD applications applications -ADD base_layer base_layer -ADD clients clients -ADD common common -ADD common_sqlite common_sqlite -ADD comms comms -ADD infrastructure infrastructure -ADD dan_layer dan_layer -ADD meta meta - -RUN cargo build --bin tari_merge_mining_proxy --release --features $FEATURES --locked - -# Create a base minimal image for the executables -FROM bitnami/minideb:bullseye as base -ARG VERSION=1.0.1 -# Disable Prompt During Packages Installation -ARG DEBIAN_FRONTEND=noninteractive -RUN apt update && apt -y install \ - apt-transport-https \ - bash \ - ca-certificates \ - curl \ - gpg \ - iputils-ping \ - less \ - libreadline8 \ - libreadline-dev \ - libsqlite3-0 \ - openssl \ - telnet - -FROM base -RUN groupadd -g 1000 tari && useradd -s /bin/bash -u 1000 -g 1000 tari -USER tari - -ENV dockerfile_version=$VERSION -ENV APP_NAME=mm_proxy APP_EXEC=tari_merge_mining_proxy - -COPY --from=builder /tari/target/release/$APP_EXEC /usr/bin/ -COPY applications/launchpad/docker_rig/start_tari_app.sh /usr/bin/start_tari_app.sh - -ENTRYPOINT [ "start_tari_app.sh", "-c", "/var/tari/config/config.toml", "-b", "/var/tari/mm_proxy" ] diff --git a/applications/launchpad/docker_rig/monerod.Dockerfile b/applications/launchpad/docker_rig/monerod.Dockerfile deleted file mode 100644 index e27aa710b7..0000000000 --- a/applications/launchpad/docker_rig/monerod.Dockerfile +++ /dev/null @@ -1,85 +0,0 @@ -# Usage: -# docker run --restart=always -v /var/data/blockchain-xmr:/root/.bitmonero \ -# -p 18080:18080 -p 18081:18081 --name=monerod -td kannix/monero-full-node - -# Binary build - -# Declare stage using linux/amd64 base image -FROM --platform=linux/amd64 bitnami/minideb:bullseye AS build-stage-amd64 - -# Platform Args -ARG MONERO_ARCH=x64 -ARG MONERO_TAR=x86_64 - -# https://github.com/monero-project/monero/releases -ARG MONERO_VERSION=0.17.3.0 -ARG MONERO_SHA256=ac18ce3d1189410a5c175984827d5d601974733303411f6142296d647f6582ce - -# Declare stage using linux/arm64 base image -FROM --platform=linux/arm64 bitnami/minideb:bullseye AS build-stage-arm64 - -# Platform Args -ARG MONERO_ARCH=armv8 -ARG MONERO_TAR=aarch64 - -# https://github.com/monero-project/monero/releases -ARG MONERO_VERSION=0.17.3.0 -ARG MONERO_SHA256=8fdb5761f6f4345dc670d184144ce8c2fa56eeb1609ed169e79b202fcca20f7d - -# Declare TARGETARCH to make it available -ARG TARGETARCH -# Select final stage based on TARGETARCH ARG -FROM build-stage-${TARGETARCH} as build-stage-download - -ARG BUILDPLATFORM - -# Bring over the Args from platform selection -ARG MONERO_ARCH -ARG MONERO_TAR -ARG MONERO_VERSION -ARG MONERO_SHA256 - -RUN apt-get update && apt-get install -y curl bzip2 - -WORKDIR /root - -RUN curl https://dlsrc.getmonero.org/cli/monero-linux-$MONERO_ARCH-v$MONERO_VERSION.tar.bz2 -O &&\ - echo "$MONERO_SHA256 monero-linux-$MONERO_ARCH-v$MONERO_VERSION.tar.bz2" | sha256sum -c - &&\ - tar -xvf monero-linux-$MONERO_ARCH-v$MONERO_VERSION.tar.bz2 &&\ - rm monero-linux-$MONERO_ARCH-v$MONERO_VERSION.tar.bz2 &&\ - cp ./monero-$MONERO_TAR-linux-gnu-v$MONERO_VERSION/monerod . &&\ - rm -r monero-* - -FROM bitnami/minideb:bullseye AS runtime-stage - -# Bring over the Args from platform selection -ARG MONERO_VERSION -ARG VERSION=1.0.1 - -RUN groupadd -g 1000 tari && useradd -ms /bin/bash -u 1000 -g 1000 tari \ - && mkdir -p /home/tari/.bitmonero \ - && chown -R tari:tari /home/tari/.bitmonero -USER tari -WORKDIR /home/tari - -COPY --chown=tari:tari --from=build-stage-download /root/monerod /home/tari/monerod - -# blockchain location -VOLUME /home/tari/.bitmonero -ENV dockerfile_version=$VERSION -ENV dockerfile_build_arch=$BUILDPLATFORM -ENV monero_version=$MONERO_VERSION - -# Expose p2p port -EXPOSE 18080 - -EXPOSE 18081 - -# Expose restricted RPC port -EXPOSE 18089 - -# Add HEALTHCHECK against get_info endpoint -HEALTHCHECK --interval=30s --timeout=5s CMD curl --fail http://localhost:18089/get_info || exit 1 - -ENTRYPOINT ["./monerod"] -CMD ["--non-interactive", "--restricted-rpc", "--rpc-bind-ip=0.0.0.0", "--rpc-restricted-bind-port=18089", "--confirm-external-bind", "--enable-dns-blocklist", "--out-peers=16"] diff --git a/applications/launchpad/docker_rig/quick_start.sh b/applications/launchpad/docker_rig/quick_start.sh deleted file mode 100755 index 990eeee809..0000000000 --- a/applications/launchpad/docker_rig/quick_start.sh +++ /dev/null @@ -1,90 +0,0 @@ -#!/bin/bash -# -# Docker Start Script for Tari applications -# The docker compose environment should set the following envars -# - APP_NAME - the name of the app to run. This var is used to set the location of log files, and app-specific config -# - APP_EXEC - the name of the application executable. Just the name is enough, since the Dockerfile will put it in /usr/bin -# - CREATE_CONFIG - set to 1 if we should write a default config file if one is missing. -# - CREATE_ID - set to 1 if we should create an id file for this application if one is missing. It will be called -# {network}_{app_name}_id.json -# - WAIT_FOR_TOR - set to 1 to place a 30 second delay at the beginning of this script. -# - TARI_NETWORK - the Tari network to configure the docker rig for -# - -# Export these variables to the environment -START_TOR=${START_TOR:-1} -START_BASE_NODE=${START_BASE_NODE:-1} -START_WALLET=${START_WALLET:-1} -START_MINER=${START_MINER:-1} -USE_OWN_MODEROD=${USE_OWN_MODEROD:-0} -START_MONERO_MM=${START_MONERO_MM:-1} - -CREATE_CONFIG=${CREATE_CONFIG:-0} -CREATE_ID=${CREATE_ID:-0} -NETWORK=${TARI_NETWORK:-dibbler} -CONFIG=$DATA_FOLDER/config - -check_data_folder() { - if [[ ! -d "$DATA_FOLDER" ]]; then - echo "Creating data folder $DATA_FOLDER.." - mkdir -p "$DATA_FOLDER/config" - mkdir -p "$DATA_FOLDER/base_node" - mkdir -p "$DATA_FOLDER/tor" - mkdir -p "$DATA_FOLDER/xmrig" - mkdir -p "$DATA_FOLDER/monerod" - mkdir -p "$DATA_FOLDER/mm_proxy" - cp log4rs.yml config.toml "$DATA_FOLDER/config/" - SETUP=1 - echo "Done." - else - echo "Using existing data folder $DATA_FOLDER" - fi -} - -check_data_folder - -echo "network: $NETWORK" -echo "Setup: $SETUP" - -export DATA_FOLDER=$DATA_FOLDER -export WAIT_FOR_TOR=$WAIT_FOR_TOR -export TARI_NETWORK=$NETWORK - -if [[ $SETUP == 1 ]]; then - echo "Creating identity files and default config file" - docker compose run --rm base_node --init -fi - -if [[ $START_TOR == 1 ]]; then - docker compose up -d tor - WAIT_FOR_TOR=10 -fi - -if [[ $START_BASE_NODE == 1 ]]; then - echo "Starting Base Node" - export WAIT_FOR_TOR=$WAIT_FOR_TOR - docker compose up -d base_node -fi - -if [[ $START_WALLET == 1 ]]; then - echo "Starting Wallet" - export WAIT_FOR_TOR=0 - docker compose up -d wallet -fi - -if [[ $START_MINER == 1 ]]; then - echo "Starting SHA3 Miner" - export WAIT_FOR_TOR=0 - docker compose up -d sha3_miner -fi - -if [[ $USE_OWN_MONEROD == 1 ]]; then - echo "Local MoneroD is implemented yet!" - exit 1 -fi - -if [[ $START_MONERO_MM == 1 ]]; then - echo "Starting Monero Merge miner" - export WAIT_FOR_TOR=0 - docker compose up -d mm_proxy xmrig -fi diff --git a/applications/launchpad/docker_rig/run_frontail.sh b/applications/launchpad/docker_rig/run_frontail.sh deleted file mode 100755 index 7687a3ceb3..0000000000 --- a/applications/launchpad/docker_rig/run_frontail.sh +++ /dev/null @@ -1,3 +0,0 @@ -#!/bin/sh -echo Starting frontail log viewer: $@ -frontail $@ \ No newline at end of file diff --git a/applications/launchpad/docker_rig/sha3_miner.Dockerfile b/applications/launchpad/docker_rig/sha3_miner.Dockerfile deleted file mode 100644 index 0e9c378438..0000000000 --- a/applications/launchpad/docker_rig/sha3_miner.Dockerfile +++ /dev/null @@ -1,87 +0,0 @@ -FROM rust:1.60-buster as builder - -ARG DEBIAN_FRONTEND=noninteractive -RUN apt update && apt -y install \ - apt-transport-https \ - bash \ - ca-certificates \ - curl \ - gpg \ - iputils-ping \ - less \ - libreadline-dev \ - libsqlite3-0 \ - openssl \ - telnet \ - cargo \ - clang \ - cmake - -RUN apt update && apt upgrade -y && apt clean - - -WORKDIR /tari - -# Adding only necessary things up front and copying the entrypoint script last -# to take advantage of layer caching in docker -ADD Cargo.lock applications/deps_only/Cargo.lock -ADD rust-toolchain.toml . - -ARG ARCH=native -ARG FEATURES=avx2 -ENV RUSTFLAGS="-C target_cpu=$ARCH" -ENV ROARING_ARCH=$ARCH -ENV CARGO_HTTP_MULTIPLEXING=false - -# Caches downloads across docker builds -ADD applications/deps_only applications/deps_only -WORKDIR applications/deps_only -RUN cargo build --bin deps_only --release - -WORKDIR /tari - -ADD Cargo.toml . -ADD Cargo.lock . -ADD applications applications -ADD base_layer base_layer -ADD clients clients -ADD common common -ADD common_sqlite common_sqlite -ADD comms comms -ADD infrastructure infrastructure -ADD dan_layer dan_layer -ADD meta meta - -RUN cargo build --bin tari_miner --release --features $FEATURES --locked - -# Create a base minimal image for the executables -FROM bitnami/minideb:bullseye as base -ARG VERSION=1.0.1 -# Disable Prompt During Packages Installation -ARG DEBIAN_FRONTEND=noninteractive -RUN apt update && apt -y install \ - apt-transport-https \ - bash \ - ca-certificates \ - curl \ - gpg \ - iputils-ping \ - less \ - libreadline8 \ - libreadline-dev \ - libsqlite3-0 \ - openssl \ - telnet - -FROM base -RUN groupadd -g 1000 tari && useradd -s /bin/bash -u 1000 -g 1000 tari -USER tari - -ENV dockerfile_version=$VERSION -ENV APP_NAME=sha3_miner APP_EXEC=tari_miner - -COPY --from=builder /tari/target/release/$APP_EXEC /usr/bin/ -COPY applications/launchpad/docker_rig/start_tari_app.sh /usr/bin/start_tari_app.sh - -ENTRYPOINT [ "start_tari_app.sh", "-c", "/var/tari/config/config.toml", "-b", "/var/tari/sha3_miner" ] - diff --git a/applications/launchpad/docker_rig/start_tari_app.sh b/applications/launchpad/docker_rig/start_tari_app.sh deleted file mode 100755 index abf181a1b6..0000000000 --- a/applications/launchpad/docker_rig/start_tari_app.sh +++ /dev/null @@ -1,38 +0,0 @@ -#!/bin/bash -# -# Docker Start Script for Tari applications -# The docker compose environment should set the following envars -# - APP_NAME - the name of the app to run. This var is used to set the location of log files, and app-specific config -# - APP_EXEC - the name of the application executable. Just the name is enough, since the Dockerfile will put it in /usr/bin -# - WAIT_FOR_TOR - set to the delay in seconds to pause at the beginning of this script. -# - -APP_NAME=${APP_NAME:-base_node} -APP_EXEC=${APP_EXEC:-tari_base_node} -WAIT_FOR_TOR=${WAIT_FOR_TOR:-0} -TARI_BASE=/var/tari/$APP_NAME -CONFIG=/var/tari/config -USER_ID=${USER_ID:-1000} -GROUP_ID=${GROUP_ID:-1000} - -echo "Starting $APP_NAME with following docker environment:" -echo "executable: $APP_EXEC" -echo "WAIT_FOR_TOR: $WAIT_FOR_TOR" -echo "base folder (in container): $TARI_BASE" -echo "config folder (in container): $CONFIG" - -if [[ $WAIT_FOR_TOR != 0 ]]; then - echo "Waiting $WAIT_FOR_TOR seconds for Tor to start up" - sleep "$WAIT_FOR_TOR" -fi - -if [[ ! -d "$TARI_BASE" ]]; then - mkdir -p "$TARI_BASE" -fi - -cd "$TARI_BASE" || exit 1 - -echo "Starting ${APP_NAME}..." -echo Command: $APP_EXEC "$@" -$APP_EXEC "$@" || exit 1 - diff --git a/applications/launchpad/docker_rig/tarilabs.Dockerfile b/applications/launchpad/docker_rig/tarilabs.Dockerfile deleted file mode 100644 index 88d2a98b14..0000000000 --- a/applications/launchpad/docker_rig/tarilabs.Dockerfile +++ /dev/null @@ -1,154 +0,0 @@ -# syntax = docker/dockerfile:1.3 -FROM --platform=$BUILDPLATFORM rust:1.60-bullseye as builder - -# Declare to make available -ARG BUILDPLATFORM -ARG BUILDOS -ARG BUILDARCH -ARG BUILDVARIANT -ARG TARGETPLATFORM -ARG TARGETOS -ARG TARGETARCH -ARG TARGETVARIANT - -# Disable anti-cache -RUN rm -f /etc/apt/apt.conf.d/docker-clean; echo 'Binary::apt::APT::Keep-Downloaded-Packages "true";' > /etc/apt/apt.conf.d/keep-cache -# https://github.com/moby/buildkit/blob/master/frontend/dockerfile/docs/syntax.md#run---mounttypecache -RUN --mount=type=cache,id=build-apt-cache-${BUILDOS}-${BUILDARCH}${BUILDVARIANT},sharing=locked,target=/var/cache/apt \ - --mount=type=cache,id=build-apt-lib-${BUILDOS}-${BUILDARCH}${BUILDVARIANT},sharing=locked,target=/var/lib/apt \ - apt update && apt-get install -y \ - apt-transport-https \ - bash \ - ca-certificates \ - curl \ - gpg \ - iputils-ping \ - less \ - libreadline-dev \ - libsqlite3-0 \ - openssl \ - telnet \ - cargo \ - clang \ - cmake - -ARG ARCH=native -#ARG FEATURES=avx2 -ARG FEATURES=safe -ENV RUSTFLAGS="-C target_cpu=$ARCH" -ENV ROARING_ARCH=$ARCH -ENV CARGO_HTTP_MULTIPLEXING=false - -ARG APP_NAME=wallet -ARG APP_EXEC=tari_console_wallet - -# GNU C compiler for the arm64 architecture and GNU C++ compiler -#RUN if [[ "${TARGETPLATFORM}" == "linux/arm64" ]] ; then \ -RUN if [ "${TARGETARCH}" = "arm64" ] ; then \ - echo "Setup ARM64" && \ - apt update && \ - apt-get install -y gcc-aarch64-linux-gnu g++-aarch64-linux-gnu && \ - rustup target add aarch64-unknown-linux-gnu && \ - rustup toolchain install stable-aarch64-unknown-linux-gnu ; \ - else \ - echo "Setup x86-64" ; \ - fi - -WORKDIR /tari - -ADD Cargo.toml . -ADD Cargo.lock . -ADD applications applications -ADD base_layer base_layer -ADD clients clients -ADD common common -ADD common_sqlite common_sqlite -ADD comms comms -ADD infrastructure infrastructure -ADD dan_layer dan_layer -ADD meta meta - -RUN --mount=type=cache,id=rust-git-${TARGETOS}-${TARGETARCH}${TARGETVARIANT},sharing=locked,target=/home/rust/.cargo/git \ - --mount=type=cache,id=rust-home-registry-${TARGETOS}-${TARGETARCH}${TARGETVARIANT},sharing=locked,target=/home/rust/.cargo/registry \ - --mount=type=cache,id=rust-local-registry-${TARGETOS}-${TARGETARCH}${TARGETVARIANT},sharing=locked,target=/usr/local/cargo/registry \ - --mount=type=cache,id=rust-src-target-${TARGETOS}-${TARGETARCH}${TARGETVARIANT},sharing=locked,target=/home/rust/src/target \ - --mount=type=cache,id=rust-target-${TARGETOS}-${TARGETARCH}${TARGETVARIANT},sharing=locked,target=/tari/target \ - if [ "${TARGETARCH}" = "arm64" ] ; then \ - export BUILD_TARGET="aarch64-unknown-linux-gnu/" && \ - export RUST_TARGET="--target=aarch64-unknown-linux-gnu" && \ - export ARCH=generic && \ - export FEATURES=safe && \ - export CARGO_TARGET_AARCH64_UNKNOWN_LINUX_GNU_LINKER=aarch64-linux-gnu-gcc && \ - export CC_aarch64_unknown_linux_gnu=aarch64-linux-gnu-gcc && \ - export CXX_aarch64_unknown_linux_gnu=aarch64-linux-gnu-g++ && \ - export BINDGEN_EXTRA_CLANG_ARGS="--sysroot /usr/aarch64-linux-gnu/include/" && \ - export RUSTFLAGS="-C target_cpu=generic" && \ - export ROARING_ARCH=generic ; \ - fi && \ - cargo update && \ - cargo build ${RUST_TARGET} \ - --bin ${APP_EXEC} --release --features $FEATURES --locked && \ - # Copy executable out of the cache so it is available in the runtime image. - cp -v /tari/target/${BUILD_TARGET}release/${APP_EXEC} /tari/${APP_EXEC} - -# Create runtime base minimal image for the target platform executables -FROM --platform=$TARGETPLATFORM bitnami/minideb:bullseye as runtime - -ARG BUILDPLATFORM -ARG TARGETOS -ARG TARGETARCH -ARG TARGETVARIANT - -ARG VERSION=1.0.1 - -ARG APP_NAME -ARG APP_EXEC - -# Disable Prompt During Packages Installation -ARG DEBIAN_FRONTEND=noninteractive - -# Disable anti-cache -RUN rm -f /etc/apt/apt.conf.d/docker-clean; echo 'Binary::apt::APT::Keep-Downloaded-Packages "true";' > /etc/apt/apt.conf.d/keep-cache -RUN --mount=type=cache,id=runtime-apt-cache-${TARGETOS}-${TARGETARCH}${TARGETVARIANT},sharing=locked,target=/var/cache/apt \ - --mount=type=cache,id=runtime-apt-lib-${TARGETOS}-${TARGETARCH}${TARGETVARIANT},sharing=locked,target=/var/lib/apt \ - apt update && apt-get --no-install-recommends install -y \ - apt-transport-https \ - bash \ - ca-certificates \ - curl \ - gpg \ - iputils-ping \ - less \ - libreadline8 \ - libreadline-dev \ - libsqlite3-0 \ - openssl \ - telnet - -RUN groupadd -g 1000 tari && useradd -s /bin/bash -u 1000 -g 1000 tari - -ENV dockerfile_version=$VERSION -ENV dockerfile_build_arch=$BUILDPLATFORM -ENV APP_NAME=${APP_NAME:-wallet} -ENV APP_EXEC=${APP_EXEC:-tari_console_wallet} - -RUN mkdir -p "/var/tari/${APP_NAME}" && \ - chown -R tari.tari "/var/tari/${APP_NAME}" - -# Setup blockchain path for base_node only -RUN if [ "${APP_NAME}" = "base_node" ] ; then \ - echo "Base_node bits" && \ - mkdir /blockchain && \ - chown -R tari.tari /blockchain && \ - chmod -R 0700 /blockchain ; \ - else \ - echo "Not base_node" ; \ - fi - -USER tari - -COPY --from=builder /tari/$APP_EXEC /usr/bin/ -COPY applications/launchpad/docker_rig/start_tari_app.sh /usr/bin/start_tari_app.sh - -ENTRYPOINT [ "start_tari_app.sh", "-c", "/var/tari/config/config.toml", "-b", "/var/tari/${APP_NAME}" ] -# CMD [ "--non-interactive-mode" ] diff --git a/applications/launchpad/docker_rig/temp/config/config.toml b/applications/launchpad/docker_rig/temp/config/config.toml deleted file mode 100644 index b599edbcc6..0000000000 --- a/applications/launchpad/docker_rig/temp/config/config.toml +++ /dev/null @@ -1,387 +0,0 @@ -[common] -network = "dibbler" -buffer_rate_limit_console_wallet = 1000 -dedup_cache_capacity = 25000 -rpc_max_simultaneous_sessions = 10000 - -[common.igor] -peer_seeds = [ - # igor - "8e7eb81e512f3d6347bf9b1ca9cd67d2c8e29f2836fc5bd608206505cc72af34::/onion3/l4wouomx42nezhzexjdzfh7pcou5l7df24ggmwgekuih7tkv2rsaokqd:18141", - "00b35047a341401bcd336b2a3d564280a72f6dc72ec4c739d30c502acce4e803::/onion3/ojhxd7z6ga7qrvjlr3px66u7eiwasmffnuklscbh5o7g6wrbysj45vid:18141", - "40a9d8573745072534bce7d0ecafe882b1c79570375a69841c08a98dee9ecb5f::/onion3/io37fylc2pupg4cte4siqlsmuszkeythgjsxs2i3prm6jyz2dtophaad:18141", - "126c7ee64f71aca36398b977dd31fbbe9f9dad615df96473fb655bef5709c540::/onion3/6ilmgndocop7ybgmcvivbdsetzr5ggj4hhsivievoa2dx2b43wqlrlid:18141", -] - -dns_seeds =["seeds.igor.tari.com"] -# dns_seeds_name_server = "1.1.1.1:853/cloudfare-dns.com" -dns_seeds_use_dnssec = false - -[common.dibbler] -dns_seeds =["seeds.dibbler.tari.com"] -peer_seeds = [ - # 333388d1cbe3e2bd17453d052f - "c2eca9cf32261a1343e21ed718e79f25bfc74386e9305350b06f62047f519347::/onion3/6yxqk2ybo43u73ukfhyc42qn25echn4zegjpod2ccxzr2jd5atipwzqd:18141", - # 555575715a49fc242d756e52ca - "42fcde82b44af1de95a505d858cb31a422c56c4ac4747fbf3da47d648d4fc346::/onion3/2l3e7ysmihc23zybapdrsbcfg6omtjtfkvwj65dstnfxkwtai2fawtyd:18141", - # 77771f53be07fab4be5f1e1ff7 - "50e6aa8f6c50f1b9d9b3d438dfd2a29cfe1f3e3a650bd9e6b1e10f96b6c38f4d::/onion3/7s6y3cz5bnewlj5ypm7sekhgvqjyrq4bpaj5dyvvo7vxydj7hsmyf5ad:18141", - # 9999016f1f3a6162dddf5a45aa - "36a9df45e1423b5315ffa7a91521924210c8e1d1537ad0968450f20f21e5200d::/onion3/v24qfheti2rztlwzgk6v4kdbes3ra7mo3i2fobacqkbfrk656e3uvnid:18141", - # bbbb8358387d81c388fadb4649 - "be128d570e8ec7b15c101ee1a56d6c56dd7d109199f0bd02f182b71142b8675f::/onion3/ha422qsy743ayblgolui5pg226u42wfcklhc5p7nbhiytlsp4ir2syqd:18141", - # eeeeb0a943ed143e613a135392 - "3e0321c0928ca559ab3c0a396272dfaea705efce88440611a38ff3898b097217::/onion3/sl5ledjoaisst6d4fh7kde746dwweuge4m4mf5nkzdhmy57uwgtb7qqd:18141", - # 66664a0f95ce468941bb9de228 - "b0f797e7413b39b6646fa370e8394d3993ead124b8ba24325c3c07a05e980e7e::/ip4/35.177.93.69/tcp/18189", - # 22221bf814d5e524fce9ba5787 - "0eefb45a4de9484eca74846a4f47d2c8d38e76be1fec63b0112bd00d297c0928::/ip4/13.40.98.39/tcp/18189", - # 4444a0efd8388739d563bdd979 - "544ed2baed414307e119d12894e27f9ddbdfa2fd5b6528dc843f27903e951c30::/ip4/13.40.189.176/tcp/18189" -] - -[base_node] -network = "dibbler" -grpc_enabled = true -grpc_address = "/ip4/0.0.0.0/tcp/18142" -track_reorgs = true - -[base_node.dibbler] -db_type = "lmdb" -flood_ban_max_msg_count = 100_000 -data_dir = "/blockchain/dibbler" -force_sync_peers = [] -allow_test_addresses = false -transport = "tor" -use_libtor = false -tor_control_address = "/dns4/tor/tcp/9051" - -# Overridden by the docker environment variables -tor_control_auth = "password=tari" -tor_onion_port = 18141 -tor_forward_address = "/dns4/base_node/tcp/18189" -tor_socks_address_override="/dns4/tor/tcp/9050" -console_wallet_identity_file = "config/dibbler/console_wallet_id.json" -base_node_identity_file = "config/dibbler/base_node_id.json" -base_node_tor_identity_file = "config/dibbler/base_node_tor.json" -console_wallet_tor_identity_file = "config/dibbler/console_wallet_tor.json" - -[mempool.dibbler] - -[base_node.igor] -db_type = "lmdb" -flood_ban_max_msg_count = 100_000 -data_dir = "/blockchain/igor" -force_sync_peers = [] -allow_test_addresses = false -transport = "tor" -use_libtor = false -tor_control_address = "/ip4/tor/tcp/9051" - -# Overridden by the docker environment variables -tor_control_auth = "password=tari" -tor_onion_port = 18141 -tor_forward_address = "/dns4/base_node/tcp/18189" -tor_socks_address_override="/dns4/tor/tcp/9050" - -console_wallet_identity_file = "config/igor/console_wallet_id.json" -base_node_identity_file = "config/igor/base_node_id.json" -base_node_tor_identity_file = "config/igor/base_node_tor.json" -console_wallet_tor_identity_file = "config/igor/console_wallet_tor.json" - -# Do these go here or in common? -grpc_enabled = true -grpc_base_node_address = "0.0.0.0:18142" - - -[mempool.igor] -[wallet] -# Override common.network for wallet -# network = "dibbler" - -wallet_db_file = "wallet/igor/wallet.dat" -console_wallet_db_file = "wallet/igor/console-wallet.dat" -grpc_enabled = true -grpc_address = "/ip4/127.0.0.1/tcp/18143" - -# Console wallet password -# Should you wish to start your console wallet without typing in your password, the following options are available: -# 1. Start the console wallet with the --password=secret argument, or -# 2. Set the environment variable TARI_WALLET_PASSWORD=secret before starting the console wallet, or -# 3. Set the "password" key in this [wallet] section of the config -# password = "secret" - -# WalletNotify -# Allows you to execute a script or program when these transaction events are received by the console wallet: -# - transaction received -# - transaction sent -# - transaction cancelled -# - transaction mined but unconfirmed -# - transaction mined and confirmed -# An example script is available here: applications/tari_console_wallet/src/notifier/notify_example.sh -# notify = "/path/to/script" - -# This is the timeout period that will be used to monitor TXO queries to the base node (default = 60). Larger values -# are needed for wallets with many (>1000) TXOs to be validated. -base_node_query_timeout = 180 -# The amount of seconds added to the current time (Utc) which will then be used to check if the message has -# expired or not when processing the message (default = 10800). -#saf_expiry_duration = 10800 -# This is the number of block confirmations required for a transaction to be considered completely mined and -# confirmed. (default = 3) -#transaction_num_confirmations_required = 3 -# This is the timeout period that will be used for base node broadcast monitoring tasks (default = 60) -transaction_broadcast_monitoring_timeout = 180 -# This is the timeout period that will be used for chain monitoring tasks (default = 60) -#transaction_chain_monitoring_timeout = 60 -# This is the timeout period that will be used for sending transactions directly (default = 20) -transaction_direct_send_timeout = 180 -# This is the timeout period that will be used for sending transactions via broadcast mode (default = 60) -transaction_broadcast_send_timeout = 180 -# This is the size of the event channel used to communicate transaction status events to the wallet's UI. A busy console -# wallet doing thousands of bulk payments or used for stress testing needs a fairly big size (>10000) (default = 1000). -transaction_event_channel_size = 25000 -# This is the size of the event channel used to communicate base node events to the wallet. A busy console -# wallet doing thousands of bulk payments or used for stress testing needs a fairly big size (>3000) (default = 250). -base_node_event_channel_size = 3500 -# This is the size of the event channel used to communicate output manager events to the wallet. A busy console -# wallet doing thousands of bulk payments or used for stress testing needs a fairly big size (>3000) (default = 250). -output_manager_event_channel_size = 3500 -# This is the size of the event channel used to communicate base node update events to the wallet. A busy console -# wallet doing thousands of bulk payments or used for stress testing needs a fairly big size (>300) (default = 50). -base_node_update_publisher_channel_size = 500 -# If a large amount of tiny valued uT UTXOs are used as inputs to a transaction, the fee may be larger than -# the transaction amount. Set this value to `false` to allow spending of "dust" UTXOs for small valued -# transactions (default = true). -#prevent_fee_gt_amount = false -# This option specifies the transaction routing mechanism as being directly between wallets, making -# use of store and forward or using any combination of these. -# (options: "DirectOnly", "StoreAndForwardOnly", DirectAndStoreAndForward". default: "DirectAndStoreAndForward"). -#transaction_routing_mechanism = "DirectAndStoreAndForward" - -# When running the console wallet in command mode, use these values to determine what "stage" and timeout to wait -# for sent transactions. -# The stages are: -# - "DirectSendOrSaf" - The transaction was initiated and was accepted via Direct Send or Store And Forward. -# - "Negotiated" - The recipient replied and the transaction was negotiated. -# - "Broadcast" - The transaction was broadcast to the base node mempool. -# - "MinedUnconfirmed" - The transaction was successfully detected as mined but unconfirmed on the blockchain. -# - "Mined" - The transaction was successfully detected as mined and confirmed on the blockchain. - -# The default values are: "Broadcast", 300 -#command_send_wait_stage = "Broadcast" -#command_send_wait_timeout = 300 - -# The base nodes that the wallet should use for service requests and tracking chain state. -# base_node_service_peers = ["public_key::net_address", ...] -# base_node_service_peers = ["e856839057aac496b9e25f10821116d02b58f20129e9b9ba681b830568e47c4d::/onion3/exe2zgehnw3tvrbef3ep6taiacr6sdyeb54be2s25fpru357r4skhtad:18141"] - -# Configuration for the wallet's base node service -# The refresh interval, defaults to 10 seconds -base_node_service_refresh_interval = 30 -# The maximum age of service requests in seconds, requests older than this are discarded -base_node_service_request_max_age = 180 - -#[base_node.transport.tor] -#control_address = "/ip4/127.0.0.1/tcp/9051" -#control_auth_type = "none" # or "password" -# Required for control_auth_type = "password" -#control_auth_password = "super-secure-password" - -# Wallet configuration options for dibbler -[wallet.dibbler] -# -------------- Transport configuration -------------- -# Use TCP to connect to the Tari network. This transport can only communicate with TCP/IP addresses, so peers with -# e.g. tor onion addresses will not be contactable. -#transport = "tcp" -# The address and port to listen for peer connections over TCP. -#tcp_listener_address = "/ip4/0.0.0.0/tcp/18188" -# Configures a tor proxy used to connect to onion addresses. All other traffic uses direct TCP connections. -# This setting is optional however, if it is not specified, this node will not be able to connect to nodes that -# only advertise an onion address. -#tcp_tor_socks_address = "/ip4/127.0.0.1/tcp/36050" -#tcp_tor_socks_auth = "none" - -# Configures the node to run over a tor hidden service using the Tor proxy. This transport recognises ip/tcp, -# onion v2, onion v3 and dns addresses. -transport = "tor" - -# Spin up and use a built-in Tor instance. This only works on macos/linux and you must comment out tor_control_address below. -# This requires that the wallet was built with the optional "libtor" feature flag. -#use_libtor = true - -# Address of the tor control server -tor_control_address = "/ip4/127.0.0.1/tcp/9051" -# Authentication to use for the tor control server -#tor_control_auth = "none" # or "password=xxxxxx" -# The onion port to use. -#tor_onion_port = 18141 -# The address to which traffic on the node's onion address will be forwarded -# tor_forward_address = "/ip4/127.0.0.1/tcp/0" -# Instead of attemping to get the SOCKS5 address from the tor control port, use this one. The default is to -# use the first address returned by the tor control port (GETINFO /net/listeners/socks). -#tor_socks_address_override= - -# Use a SOCKS5 proxy transport. This transport recognises any addresses supported by the proxy. -#transport = "socks5" -# The address of the SOCKS5 proxy -#socks5_proxy_address = "/ip4/127.0.0.1/tcp/9050" -# The address to which traffic will be forwarded -#socks5_listener_address = "/ip4/127.0.0.1/tcp/18188" -#socks5_auth = "none" # or "username_password=username:xxxxxxx" - -# Optionally bind an additional TCP socket for inbound Tari P2P protocol commms. -# Use cases include: -# - allowing wallets to locally connect to their base node, rather than through tor, when used in conjunction with `tor_proxy_bypass_addresses` -# - multiple P2P addresses, one public over DNS and one private over TOR -# - a "bridge" between TOR and TCP-only nodes -# auxiliary_tcp_listener_address = "/ip4/127.0.0.1/tcp/9998" - -# When these peer addresses are encountered when dialing another peer, the tor proxy is bypassed and the connection is made -# directly over TCP. /ip4, /ip6, /dns, /dns4 and /dns6 are supported. -# tor_proxy_bypass_addresses = ["/dns4/my-foo-base-node/tcp/9998"] -# When using the tor transport and set to true, outbound TCP connections bypass the tor proxy. Defaults to false for better privacy -# tor_proxy_bypass_for_outbound_tcp = false - -# Wallet configuration options for igor -[wallet.igor] -transport = "tor" -use_libtor = false -tor_control_address = "/ip4/tor/tcp/9051" -tor_control_auth = "password=tari" -tor_onion_port = 18141 -# The address to which traffic on the node's onion address will be forwarded -# tor_forward_address = "/ip4/127.0.0.1/tcp/0" -# Instead of attemping to get the SOCKS5 address from the tor control port, use this one. The default is to -# use the first address returned by the tor control port (GETINFO /net/listeners/socks). -#tor_socks_address_override= - -# Use a SOCKS5 proxy transport. This transport recognises any addresses supported by the proxy. -#transport = "socks5" -# The address of the SOCKS5 proxy -#socks5_proxy_address = "/ip4/127.0.0.1/tcp/9050" -# The address to which traffic will be forwarded -#socks5_listener_address = "/ip4/127.0.0.1/tcp/18188" -#socks5_auth = "none" # or "username_password=username:xxxxxxx" - -# Optionally bind an additional TCP socket for inbound Tari P2P protocol commms. -# Use cases include: -# - allowing wallets to locally connect to their base node, rather than through tor, when used in conjunction with `tor_proxy_bypass_addresses` -# - multiple P2P addresses, one public over DNS and one private over TOR -# - a "bridge" between TOR and TCP-only nodes -# auxiliary_tcp_listener_address = "/ip4/127.0.0.1/tcp/9998" - -# When these peer addresses are encountered when dialing another peer, the tor proxy is bypassed and the connection is made -# directly over TCP. /ip4, /ip6, /dns, /dns4 and /dns6 are supported. -# tor_proxy_bypass_addresses = ["/dns4/my-foo-base-node/tcp/9998"] -# When using the tor transport and set to true, outbound TCP connections bypass the tor proxy. Defaults to false for better privacy -# tor_proxy_bypass_for_outbound_tcp = false; - -######################################################################################################################## -# # -# Miner Configuration Options # -# # -######################################################################################################################## - -[miner] -# Number of mining threads -# Default: number of logical CPU cores -#num_mining_threads=8 - -# GRPC address of base node -# Default: value from `base_node.grpc_base_node_address` -#base_node_grpc_address = "127.0.0.1:18142" - -# GRPC address of console wallet -# Default: value from `wallet.grpc_address` -#wallet_grpc_address = "127.0.0.1:18143" - -# Start mining only when base node is bootstrapped -# and current block height is on the tip of network -# Default: true -#mine_on_tip_only=true - -# Will check tip with node every N seconds and restart mining -# if height already taken and option `mine_on_tip_only` is set -# to true -# Default: 30 seconds -#validate_tip_timeout_sec=30 - -# Stratum Mode configuration -# mining_pool_address = "miningcore.tari.com:3052" -# mining_wallet_address = "YOUR_WALLET_PUBLIC_KEY" -# mining_worker_name = "worker1" - -######################################################################################################################## -# # -# Merge Mining Configuration Options # -# # -######################################################################################################################## - -[merge_mining_proxy.dibbler] - -# URL to monerod -monerod_url = [ # stagenet - "http://stagenet.xmr-tw.org:38081", - "http://stagenet.community.xmr.to:38081", - "http://monero-stagenet.exan.tech:38081", - "http://xmr-lux.boldsuck.org:38081", - "http://singapore.node.xmr.pm:38081", -] -#monerod_url = [ # mainnet -# "http://18.132.124.81:18081", -# "http://xmr.support:18081", -# "http://node1.xmr-tw.org:18081", -# "http://xmr.nthrow.nyc:18081", -#] - -# Address of the tari_merge_mining_proxy application -proxy_host_address = "127.0.0.1:18081" - -# In sole merged mining, the block solution is usually submitted to the Monero blockchain -# (monerod) as well as to the Tari blockchain, then this setting should be "true". With pool -# merged mining, there is no sense in submitting the solution to the Monero blockchain as the -# pool does that, then this setting should be "false". (default = true). -proxy_submit_to_origin = true - -# If authentication is being used for curl -monerod_use_auth = false - -# Username for curl -monerod_username = "" - -# Password for curl -monerod_password = "" - -# The merge mining proxy can either wait for the base node to achieve initial sync at startup before it enables mining, -# or not. If merge mining starts before the base node has achieved initial sync, those Tari mined blocks will not be -# accepted. (Default value = true; will wait for base node initial sync). -#wait_for_initial_sync_at_startup = true - -[merge_mining_proxy] -monerod_use_auth = false -monerod_username = "" -monerod_password = "" - -######################################################################################################################## -# # -# Stratum Transcoder Configuration Options # -# # -######################################################################################################################## - -[stratum_transcoder] - -# Address of the tari_stratum_transcoder application -transcoder_host_address = "127.0.0.1:7879" - - -[validator_node] -######################################################################################################################## -# # -# Validator Node Configuration Options # -# # -######################################################################################################################## - -committee = ["2ea0df3059caf4411624d6bf5b9c02238d607d2798c586b3e6c2a054da3f205a"] # cannot be of zero size -phase_timeout = 30 -template_id = "EditableMetadata" diff --git a/applications/launchpad/docker_rig/temp/config/log4rs.yml b/applications/launchpad/docker_rig/temp/config/log4rs.yml deleted file mode 100644 index 719b3ac32c..0000000000 --- a/applications/launchpad/docker_rig/temp/config/log4rs.yml +++ /dev/null @@ -1,156 +0,0 @@ -# A customised logfile configuration for running the Tari system in docker - -# timestamp [target] LEVEL message -refresh_rate: 30 seconds -appenders: - # An appender named "stdout" that writes to stdout - stdout: - kind: console - encoder: - pattern: "{d(%Y-%m-%d %H:%M:%S.%f)} [{t}] {h({l}):5} {m}{n}" - - # An appender named "network" that writes to a file with a custom pattern encoder - network: - kind: rolling_file - path: "log/network.log" - policy: - kind: compound - trigger: - kind: size - limit: 10mb - roller: - kind: fixed_window - base: 1 - count: 5 - pattern: "log/network.{}.log" - encoder: - pattern: "{d(%Y-%m-%d %H:%M:%S.%f)} [{t}] {l:5} {m}{n}" - - # An appender named "core" that writes to a file with a custom pattern encoder - core: - kind: rolling_file - path: "log/core.log" - policy: - kind: compound - trigger: - kind: size - limit: 10mb - roller: - kind: fixed_window - base: 1 - count: 5 - pattern: "log/core.{}.log" - encoder: - pattern: "{d(%Y-%m-%d %H:%M:%S.%f)} [{t}] {l:5} {m}{n}" - - # An appender named "other" that writes to a file with a custom pattern encoder - other: - kind: rolling_file - path: "log/other.log" - policy: - kind: compound - trigger: - kind: size - limit: 10mb - roller: - kind: fixed_window - base: 1 - count: 5 - pattern: "log/other.{}.log" - encoder: - pattern: "{d(%Y-%m-%d %H:%M:%S.%f)} [{t}] {l:5} {m}{n}" - -# Set the default logging level to "info" -root: - level: info - appenders: - - core - - stdout - -loggers: - # All events that get logged to core. Many of these are in separate log folders corresponding to their docker containers - tari::application: - level: debug - appenders: - - core - additive: false - base_node::app: - level: debug - appenders: - - core - additive: false - c: - level: debug - appenders: - - core - additive: false - tari: - level: debug - appenders: - - core - additive: false - wallet: - level: debug - appenders: - - core - additive: false - tari_miner: - level: debug - appenders: - - core - additive: false - tari_mm_proxy: - level: info - appenders: - - core - additive: false - - tracing: - level: info - appenders: - - other - additive: false - - # Comms messages get logged to the 'network' log - comms: - level: info - appenders: - - network - additive: false - tari_comms: - level: info - appenders: - - network - additive: false - p2p: - level: info - appenders: - - network - additive: false - yamux: - level: info - appenders: - - network - additive: false - mio: - level: error - appenders: - - network - additive: false - - # Miscellaneous events. Log these in 'other' - rustyline: - level: error - appenders: - - other - additive: false - tokio_util: - level: error - appenders: - - other - additive: false - pgp: - level: warn - appenders: - - other - additive: false diff --git a/applications/launchpad/docker_rig/tor.Dockerfile b/applications/launchpad/docker_rig/tor.Dockerfile deleted file mode 100644 index fd31fd3e16..0000000000 --- a/applications/launchpad/docker_rig/tor.Dockerfile +++ /dev/null @@ -1,23 +0,0 @@ -# Package build -FROM alpine:latest - -ARG BUILDPLATFORM -ARG VERSION=1.0.1 - -# https://pkgs.alpinelinux.org/packages?name=tor&branch=v3.16&repo=community -ARG TOR_VERSION=0.4.7.7-r1 - -RUN apk update \ - && apk upgrade \ - && apk add tor=$TOR_VERSION \ - && rm /var/cache/apk/* - -EXPOSE 9050 -EXPOSE 9051 -ENV dockerfile_version=$VERSION -ENV dockerfile_build_arch=$BUILDPLATFORM -ENV tor_version=$TOR_VERSION - -USER tor -ENTRYPOINT ["/usr/bin/tor"] -CMD ["-f", "/etc/tor/torrc"] diff --git a/applications/launchpad/docker_rig/torrc b/applications/launchpad/docker_rig/torrc deleted file mode 100644 index f313c2fe10..0000000000 --- a/applications/launchpad/docker_rig/torrc +++ /dev/null @@ -1,7 +0,0 @@ -# Standard config for the tor client - /etc/tor/torrc -SocksPort 0.0.0.0:9050 -ControlPort 0.0.0.0:9051 -CookieAuthentication 0 -ClientOnly 1 -ClientUseIPv6 1 -HashedControlPassword 16:29AAD7BADA64895D604EE18A5549712C9DADAF373B72D7DEF0D4AE97AE diff --git a/applications/launchpad/docker_rig/xmrig.Dockerfile b/applications/launchpad/docker_rig/xmrig.Dockerfile deleted file mode 100644 index e5f319a64f..0000000000 --- a/applications/launchpad/docker_rig/xmrig.Dockerfile +++ /dev/null @@ -1,61 +0,0 @@ -# Source build -FROM alpine:latest as builder - -# Declare to make available -ARG BUILDPLATFORM - -# https://github.com/xmrig/xmrig/releases -ARG XMRIG_VERSION="v6.17.0" - -RUN apk add \ - git \ - make \ - cmake \ - libstdc++ \ - gcc \ - g++ \ - automake \ - libtool \ - autoconf \ - linux-headers \ - bash - -RUN git clone --branch ${XMRIG_VERSION} https://github.com/xmrig/xmrig.git -RUN mkdir /xmrig/build -WORKDIR /xmrig/scripts -RUN bash ./build_deps.sh -WORKDIR /xmrig/build -RUN cmake .. -DXMRIG_DEPS=scripts/deps -DBUILD_STATIC=ON -RUN make -j$(nproc) - -# Create runtime base minimal image for the target platform executables -FROM --platform=$TARGETPLATFORM alpine:latest as runtime - -ARG BUILDPLATFORM - -ARG XMRIG_VERSION -ARG VERSION=1.0.1 -COPY --from=builder /xmrig/build/xmrig /usr/bin/ - -# Create a user & group -RUN addgroup -g 1000 tari && adduser -u 1000 -g 1000 -S tari -G tari -RUN mkdir -p /home/tari && chown tari.tari /home/tari -# Chown all the files to the app user. -USER tari -ENV dockerfile_version=$VERSION -ENV dockerfile_build_arch=$BUILDPLATFORM -ENV xmrig_version=$XMRIG_VERSION - -RUN echo -e "\ -{\ - \"autosave\": true,\ - \"cpu\": true,\ - \"opencl\": false,\ - \"cuda\": false,\ - \"pools\": [ \ - { \"coin\": \"monero\", \"url\": \"127.0.0.1:18081\", \"user\": \"44\", \"daemon\": true }\ - ]\ -}\ -" > /home/tari/.xmrig.json - -ENTRYPOINT [ "/usr/bin/xmrig" ] diff --git a/applications/launchpad/gui-vue/.gitignore b/applications/launchpad/gui-vue/.gitignore deleted file mode 100644 index 140d0b7681..0000000000 --- a/applications/launchpad/gui-vue/.gitignore +++ /dev/null @@ -1,24 +0,0 @@ -.DS_Store -node_modules -dist/* -!dist/.gitkeep - - -# local env files -.env.local -.env.*.local - -# Log files -npm-debug.log* -yarn-debug.log* -yarn-error.log* -pnpm-debug.log* - -# Editor directories and files -.idea -.vscode -*.suo -*.ntvs* -*.njsproj -*.sln -*.sw? diff --git a/applications/launchpad/gui-vue/README.md b/applications/launchpad/gui-vue/README.md deleted file mode 100644 index 06c4379054..0000000000 --- a/applications/launchpad/gui-vue/README.md +++ /dev/null @@ -1,24 +0,0 @@ -# gui-vue - -## Project setup -``` -npm install -``` - -### Compiles and hot-reloads for development -``` -npm run serve -``` - -### Compiles and minifies for production -``` -npm run build -``` - -### Lints and fixes files -``` -npm run lint -``` - -### Customize configuration -See [Configuration Reference](https://cli.vuejs.org/config/). diff --git a/applications/launchpad/gui-vue/babel.config.js b/applications/launchpad/gui-vue/babel.config.js deleted file mode 100644 index e9558405fd..0000000000 --- a/applications/launchpad/gui-vue/babel.config.js +++ /dev/null @@ -1,5 +0,0 @@ -module.exports = { - presets: [ - '@vue/cli-plugin-babel/preset' - ] -} diff --git a/applications/launchpad/gui-vue/dist/.gitkeep b/applications/launchpad/gui-vue/dist/.gitkeep deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/applications/launchpad/gui-vue/package-lock.json b/applications/launchpad/gui-vue/package-lock.json deleted file mode 100644 index 5f68c21858..0000000000 --- a/applications/launchpad/gui-vue/package-lock.json +++ /dev/null @@ -1,7621 +0,0 @@ -{ - "name": "gui-vue", - "version": "0.1.0", - "lockfileVersion": 1, - "requires": true, - "dependencies": { - "@achrinza/node-ipc": { - "version": "9.2.2", - "resolved": "https://registry.npmjs.org/@achrinza/node-ipc/-/node-ipc-9.2.2.tgz", - "integrity": "sha512-b90U39dx0cU6emsOvy5hxU4ApNXnE3+Tuo8XQZfiKTGelDwpMwBVgBP7QX6dGTcJgu/miyJuNJ/2naFBliNWEw==", - "dev": true, - "requires": { - "@node-ipc/js-queue": "2.0.3", - "event-pubsub": "4.3.0", - "js-message": "1.0.7" - } - }, - "@ampproject/remapping": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.1.2.tgz", - "integrity": "sha512-hoyByceqwKirw7w3Z7gnIIZC3Wx3J484Y3L/cMpXFbr7d9ZQj2mODrirNzcJa+SM3UlpWXYvKV4RlRpFXlWgXg==", - "dev": true, - "requires": { - "@jridgewell/trace-mapping": "^0.3.0" - } - }, - "@babel/code-frame": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.16.7.tgz", - "integrity": "sha512-iAXqUn8IIeBTNd72xsFlgaXHkMBMt6y4HJp1tIaK465CWLT/fG1aqB7ykr95gHHmlBdGbFeWWfyB4NJJ0nmeIg==", - "dev": true, - "requires": { - "@babel/highlight": "^7.16.7" - } - }, - "@babel/compat-data": { - "version": "7.17.7", - "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.17.7.tgz", - "integrity": "sha512-p8pdE6j0a29TNGebNm7NzYZWB3xVZJBZ7XGs42uAKzQo8VQ3F0By/cQCtUEABwIqw5zo6WA4NbmxsfzADzMKnQ==", - "dev": true - }, - "@babel/core": { - "version": "7.17.8", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.17.8.tgz", - "integrity": "sha512-OdQDV/7cRBtJHLSOBqqbYNkOcydOgnX59TZx4puf41fzcVtN3e/4yqY8lMQsK+5X2lJtAdmA+6OHqsj1hBJ4IQ==", - "dev": true, - "requires": { - "@ampproject/remapping": "^2.1.0", - "@babel/code-frame": "^7.16.7", - "@babel/generator": "^7.17.7", - "@babel/helper-compilation-targets": "^7.17.7", - "@babel/helper-module-transforms": "^7.17.7", - "@babel/helpers": "^7.17.8", - "@babel/parser": "^7.17.8", - "@babel/template": "^7.16.7", - "@babel/traverse": "^7.17.3", - "@babel/types": "^7.17.0", - "convert-source-map": "^1.7.0", - "debug": "^4.1.0", - "gensync": "^1.0.0-beta.2", - "json5": "^2.1.2", - "semver": "^6.3.0" - } - }, - "@babel/generator": { - "version": "7.17.7", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.17.7.tgz", - "integrity": "sha512-oLcVCTeIFadUoArDTwpluncplrYBmTCCZZgXCbgNGvOBBiSDDK3eWO4b/+eOTli5tKv1lg+a5/NAXg+nTcei1w==", - "dev": true, - "requires": { - "@babel/types": "^7.17.0", - "jsesc": "^2.5.1", - "source-map": "^0.5.0" - }, - "dependencies": { - "source-map": { - "version": "0.5.7", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", - "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", - "dev": true - } - } - }, - "@babel/helper-annotate-as-pure": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.16.7.tgz", - "integrity": "sha512-s6t2w/IPQVTAET1HitoowRGXooX8mCgtuP5195wD/QJPV6wYjpujCGF7JuMODVX2ZAJOf1GT6DT9MHEZvLOFSw==", - "dev": true, - "requires": { - "@babel/types": "^7.16.7" - } - }, - "@babel/helper-builder-binary-assignment-operator-visitor": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/helper-builder-binary-assignment-operator-visitor/-/helper-builder-binary-assignment-operator-visitor-7.16.7.tgz", - "integrity": "sha512-C6FdbRaxYjwVu/geKW4ZeQ0Q31AftgRcdSnZ5/jsH6BzCJbtvXvhpfkbkThYSuutZA7nCXpPR6AD9zd1dprMkA==", - "dev": true, - "requires": { - "@babel/helper-explode-assignable-expression": "^7.16.7", - "@babel/types": "^7.16.7" - } - }, - "@babel/helper-compilation-targets": { - "version": "7.17.7", - "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.17.7.tgz", - "integrity": "sha512-UFzlz2jjd8kroj0hmCFV5zr+tQPi1dpC2cRsDV/3IEW8bJfCPrPpmcSN6ZS8RqIq4LXcmpipCQFPddyFA5Yc7w==", - "dev": true, - "requires": { - "@babel/compat-data": "^7.17.7", - "@babel/helper-validator-option": "^7.16.7", - "browserslist": "^4.17.5", - "semver": "^6.3.0" - } - }, - "@babel/helper-create-class-features-plugin": { - "version": "7.17.6", - "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.17.6.tgz", - "integrity": "sha512-SogLLSxXm2OkBbSsHZMM4tUi8fUzjs63AT/d0YQIzr6GSd8Hxsbk2KYDX0k0DweAzGMj/YWeiCsorIdtdcW8Eg==", - "dev": true, - "requires": { - "@babel/helper-annotate-as-pure": "^7.16.7", - "@babel/helper-environment-visitor": "^7.16.7", - "@babel/helper-function-name": "^7.16.7", - "@babel/helper-member-expression-to-functions": "^7.16.7", - "@babel/helper-optimise-call-expression": "^7.16.7", - "@babel/helper-replace-supers": "^7.16.7", - "@babel/helper-split-export-declaration": "^7.16.7" - } - }, - "@babel/helper-create-regexp-features-plugin": { - "version": "7.17.0", - "resolved": "https://registry.npmjs.org/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.17.0.tgz", - "integrity": "sha512-awO2So99wG6KnlE+TPs6rn83gCz5WlEePJDTnLEqbchMVrBeAujURVphRdigsk094VhvZehFoNOihSlcBjwsXA==", - "dev": true, - "requires": { - "@babel/helper-annotate-as-pure": "^7.16.7", - "regexpu-core": "^5.0.1" - } - }, - "@babel/helper-define-polyfill-provider": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/@babel/helper-define-polyfill-provider/-/helper-define-polyfill-provider-0.3.1.tgz", - "integrity": "sha512-J9hGMpJQmtWmj46B3kBHmL38UhJGhYX7eqkcq+2gsstyYt341HmPeWspihX43yVRA0mS+8GGk2Gckc7bY/HCmA==", - "dev": true, - "requires": { - "@babel/helper-compilation-targets": "^7.13.0", - "@babel/helper-module-imports": "^7.12.13", - "@babel/helper-plugin-utils": "^7.13.0", - "@babel/traverse": "^7.13.0", - "debug": "^4.1.1", - "lodash.debounce": "^4.0.8", - "resolve": "^1.14.2", - "semver": "^6.1.2" - } - }, - "@babel/helper-environment-visitor": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.16.7.tgz", - "integrity": "sha512-SLLb0AAn6PkUeAfKJCCOl9e1R53pQlGAfc4y4XuMRZfqeMYLE0dM1LMhqbGAlGQY0lfw5/ohoYWAe9V1yibRag==", - "dev": true, - "requires": { - "@babel/types": "^7.16.7" - } - }, - "@babel/helper-explode-assignable-expression": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/helper-explode-assignable-expression/-/helper-explode-assignable-expression-7.16.7.tgz", - "integrity": "sha512-KyUenhWMC8VrxzkGP0Jizjo4/Zx+1nNZhgocs+gLzyZyB8SHidhoq9KK/8Ato4anhwsivfkBLftky7gvzbZMtQ==", - "dev": true, - "requires": { - "@babel/types": "^7.16.7" - } - }, - "@babel/helper-function-name": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.16.7.tgz", - "integrity": "sha512-QfDfEnIUyyBSR3HtrtGECuZ6DAyCkYFp7GHl75vFtTnn6pjKeK0T1DB5lLkFvBea8MdaiUABx3osbgLyInoejA==", - "dev": true, - "requires": { - "@babel/helper-get-function-arity": "^7.16.7", - "@babel/template": "^7.16.7", - "@babel/types": "^7.16.7" - } - }, - "@babel/helper-get-function-arity": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/helper-get-function-arity/-/helper-get-function-arity-7.16.7.tgz", - "integrity": "sha512-flc+RLSOBXzNzVhcLu6ujeHUrD6tANAOU5ojrRx/as+tbzf8+stUCj7+IfRRoAbEZqj/ahXEMsjhOhgeZsrnTw==", - "dev": true, - "requires": { - "@babel/types": "^7.16.7" - } - }, - "@babel/helper-hoist-variables": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.16.7.tgz", - "integrity": "sha512-m04d/0Op34H5v7pbZw6pSKP7weA6lsMvfiIAMeIvkY/R4xQtBSMFEigu9QTZ2qB/9l22vsxtM8a+Q8CzD255fg==", - "dev": true, - "requires": { - "@babel/types": "^7.16.7" - } - }, - "@babel/helper-member-expression-to-functions": { - "version": "7.17.7", - "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.17.7.tgz", - "integrity": "sha512-thxXgnQ8qQ11W2wVUObIqDL4p148VMxkt5T/qpN5k2fboRyzFGFmKsTGViquyM5QHKUy48OZoca8kw4ajaDPyw==", - "dev": true, - "requires": { - "@babel/types": "^7.17.0" - } - }, - "@babel/helper-module-imports": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.16.7.tgz", - "integrity": "sha512-LVtS6TqjJHFc+nYeITRo6VLXve70xmq7wPhWTqDJusJEgGmkAACWwMiTNrvfoQo6hEhFwAIixNkvB0jPXDL8Wg==", - "dev": true, - "requires": { - "@babel/types": "^7.16.7" - } - }, - "@babel/helper-module-transforms": { - "version": "7.17.7", - "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.17.7.tgz", - "integrity": "sha512-VmZD99F3gNTYB7fJRDTi+u6l/zxY0BE6OIxPSU7a50s6ZUQkHwSDmV92FfM+oCG0pZRVojGYhkR8I0OGeCVREw==", - "dev": true, - "requires": { - "@babel/helper-environment-visitor": "^7.16.7", - "@babel/helper-module-imports": "^7.16.7", - "@babel/helper-simple-access": "^7.17.7", - "@babel/helper-split-export-declaration": "^7.16.7", - "@babel/helper-validator-identifier": "^7.16.7", - "@babel/template": "^7.16.7", - "@babel/traverse": "^7.17.3", - "@babel/types": "^7.17.0" - } - }, - "@babel/helper-optimise-call-expression": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.16.7.tgz", - "integrity": "sha512-EtgBhg7rd/JcnpZFXpBy0ze1YRfdm7BnBX4uKMBd3ixa3RGAE002JZB66FJyNH7g0F38U05pXmA5P8cBh7z+1w==", - "dev": true, - "requires": { - "@babel/types": "^7.16.7" - } - }, - "@babel/helper-plugin-utils": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.16.7.tgz", - "integrity": "sha512-Qg3Nk7ZxpgMrsox6HreY1ZNKdBq7K72tDSliA6dCl5f007jR4ne8iD5UzuNnCJH2xBf2BEEVGr+/OL6Gdp7RxA==", - "dev": true - }, - "@babel/helper-remap-async-to-generator": { - "version": "7.16.8", - "resolved": "https://registry.npmjs.org/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.16.8.tgz", - "integrity": "sha512-fm0gH7Flb8H51LqJHy3HJ3wnE1+qtYR2A99K06ahwrawLdOFsCEWjZOrYricXJHoPSudNKxrMBUPEIPxiIIvBw==", - "dev": true, - "requires": { - "@babel/helper-annotate-as-pure": "^7.16.7", - "@babel/helper-wrap-function": "^7.16.8", - "@babel/types": "^7.16.8" - } - }, - "@babel/helper-replace-supers": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.16.7.tgz", - "integrity": "sha512-y9vsWilTNaVnVh6xiJfABzsNpgDPKev9HnAgz6Gb1p6UUwf9NepdlsV7VXGCftJM+jqD5f7JIEubcpLjZj5dBw==", - "dev": true, - "requires": { - "@babel/helper-environment-visitor": "^7.16.7", - "@babel/helper-member-expression-to-functions": "^7.16.7", - "@babel/helper-optimise-call-expression": "^7.16.7", - "@babel/traverse": "^7.16.7", - "@babel/types": "^7.16.7" - } - }, - "@babel/helper-simple-access": { - "version": "7.17.7", - "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.17.7.tgz", - "integrity": "sha512-txyMCGroZ96i+Pxr3Je3lzEJjqwaRC9buMUgtomcrLe5Nd0+fk1h0LLA+ixUF5OW7AhHuQ7Es1WcQJZmZsz2XA==", - "dev": true, - "requires": { - "@babel/types": "^7.17.0" - } - }, - "@babel/helper-skip-transparent-expression-wrappers": { - "version": "7.16.0", - "resolved": "https://registry.npmjs.org/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.16.0.tgz", - "integrity": "sha512-+il1gTy0oHwUsBQZyJvukbB4vPMdcYBrFHa0Uc4AizLxbq6BOYC51Rv4tWocX9BLBDLZ4kc6qUFpQ6HRgL+3zw==", - "dev": true, - "requires": { - "@babel/types": "^7.16.0" - } - }, - "@babel/helper-split-export-declaration": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.16.7.tgz", - "integrity": "sha512-xbWoy/PFoxSWazIToT9Sif+jJTlrMcndIsaOKvTA6u7QEo7ilkRZpjew18/W3c7nm8fXdUDXh02VXTbZ0pGDNw==", - "dev": true, - "requires": { - "@babel/types": "^7.16.7" - } - }, - "@babel/helper-validator-identifier": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.16.7.tgz", - "integrity": "sha512-hsEnFemeiW4D08A5gUAZxLBTXpZ39P+a+DGDsHw1yxqyQ/jzFEnxf5uTEGp+3bzAbNOxU1paTgYS4ECU/IgfDw==", - "dev": true - }, - "@babel/helper-validator-option": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.16.7.tgz", - "integrity": "sha512-TRtenOuRUVo9oIQGPC5G9DgK4743cdxvtOw0weQNpZXaS16SCBi5MNjZF8vba3ETURjZpTbVn7Vvcf2eAwFozQ==", - "dev": true - }, - "@babel/helper-wrap-function": { - "version": "7.16.8", - "resolved": "https://registry.npmjs.org/@babel/helper-wrap-function/-/helper-wrap-function-7.16.8.tgz", - "integrity": "sha512-8RpyRVIAW1RcDDGTA+GpPAwV22wXCfKOoM9bet6TLkGIFTkRQSkH1nMQ5Yet4MpoXe1ZwHPVtNasc2w0uZMqnw==", - "dev": true, - "requires": { - "@babel/helper-function-name": "^7.16.7", - "@babel/template": "^7.16.7", - "@babel/traverse": "^7.16.8", - "@babel/types": "^7.16.8" - } - }, - "@babel/helpers": { - "version": "7.17.8", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.17.8.tgz", - "integrity": "sha512-QcL86FGxpfSJwGtAvv4iG93UL6bmqBdmoVY0CMCU2g+oD2ezQse3PT5Pa+jiD6LJndBQi0EDlpzOWNlLuhz5gw==", - "dev": true, - "requires": { - "@babel/template": "^7.16.7", - "@babel/traverse": "^7.17.3", - "@babel/types": "^7.17.0" - } - }, - "@babel/highlight": { - "version": "7.16.10", - "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.16.10.tgz", - "integrity": "sha512-5FnTQLSLswEj6IkgVw5KusNUUFY9ZGqe/TRFnP/BKYHYgfh7tc+C7mwiy95/yNP7Dh9x580Vv8r7u7ZfTBFxdw==", - "dev": true, - "requires": { - "@babel/helper-validator-identifier": "^7.16.7", - "chalk": "^2.0.0", - "js-tokens": "^4.0.0" - } - }, - "@babel/parser": { - "version": "7.17.8", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.17.8.tgz", - "integrity": "sha512-BoHhDJrJXqcg+ZL16Xv39H9n+AqJ4pcDrQBGZN+wHxIysrLZ3/ECwCBUch/1zUNhnsXULcONU3Ei5Hmkfk6kiQ==" - }, - "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression/-/plugin-bugfix-safari-id-destructuring-collision-in-function-expression-7.16.7.tgz", - "integrity": "sha512-anv/DObl7waiGEnC24O9zqL0pSuI9hljihqiDuFHC8d7/bjr/4RLGPWuc8rYOff/QPzbEPSkzG8wGG9aDuhHRg==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.16.7" - } - }, - "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining/-/plugin-bugfix-v8-spread-parameters-in-optional-chaining-7.16.7.tgz", - "integrity": "sha512-di8vUHRdf+4aJ7ltXhaDbPoszdkh59AQtJM5soLsuHpQJdFQZOA4uGj0V2u/CZ8bJ/u8ULDL5yq6FO/bCXnKHw==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.16.7", - "@babel/helper-skip-transparent-expression-wrappers": "^7.16.0", - "@babel/plugin-proposal-optional-chaining": "^7.16.7" - } - }, - "@babel/plugin-proposal-async-generator-functions": { - "version": "7.16.8", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-async-generator-functions/-/plugin-proposal-async-generator-functions-7.16.8.tgz", - "integrity": "sha512-71YHIvMuiuqWJQkebWJtdhQTfd4Q4mF76q2IX37uZPkG9+olBxsX+rH1vkhFto4UeJZ9dPY2s+mDvhDm1u2BGQ==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.16.7", - "@babel/helper-remap-async-to-generator": "^7.16.8", - "@babel/plugin-syntax-async-generators": "^7.8.4" - } - }, - "@babel/plugin-proposal-class-properties": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-class-properties/-/plugin-proposal-class-properties-7.16.7.tgz", - "integrity": "sha512-IobU0Xme31ewjYOShSIqd/ZGM/r/cuOz2z0MDbNrhF5FW+ZVgi0f2lyeoj9KFPDOAqsYxmLWZte1WOwlvY9aww==", - "dev": true, - "requires": { - "@babel/helper-create-class-features-plugin": "^7.16.7", - "@babel/helper-plugin-utils": "^7.16.7" - } - }, - "@babel/plugin-proposal-class-static-block": { - "version": "7.17.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-class-static-block/-/plugin-proposal-class-static-block-7.17.6.tgz", - "integrity": "sha512-X/tididvL2zbs7jZCeeRJ8167U/+Ac135AM6jCAx6gYXDUviZV5Ku9UDvWS2NCuWlFjIRXklYhwo6HhAC7ETnA==", - "dev": true, - "requires": { - "@babel/helper-create-class-features-plugin": "^7.17.6", - "@babel/helper-plugin-utils": "^7.16.7", - "@babel/plugin-syntax-class-static-block": "^7.14.5" - } - }, - "@babel/plugin-proposal-decorators": { - "version": "7.17.8", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-decorators/-/plugin-proposal-decorators-7.17.8.tgz", - "integrity": "sha512-U69odN4Umyyx1xO1rTII0IDkAEC+RNlcKXtqOblfpzqy1C+aOplb76BQNq0+XdpVkOaPlpEDwd++joY8FNFJKA==", - "dev": true, - "requires": { - "@babel/helper-create-class-features-plugin": "^7.17.6", - "@babel/helper-plugin-utils": "^7.16.7", - "@babel/helper-replace-supers": "^7.16.7", - "@babel/plugin-syntax-decorators": "^7.17.0", - "charcodes": "^0.2.0" - } - }, - "@babel/plugin-proposal-dynamic-import": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-dynamic-import/-/plugin-proposal-dynamic-import-7.16.7.tgz", - "integrity": "sha512-I8SW9Ho3/8DRSdmDdH3gORdyUuYnk1m4cMxUAdu5oy4n3OfN8flDEH+d60iG7dUfi0KkYwSvoalHzzdRzpWHTg==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.16.7", - "@babel/plugin-syntax-dynamic-import": "^7.8.3" - } - }, - "@babel/plugin-proposal-export-namespace-from": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-export-namespace-from/-/plugin-proposal-export-namespace-from-7.16.7.tgz", - "integrity": "sha512-ZxdtqDXLRGBL64ocZcs7ovt71L3jhC1RGSyR996svrCi3PYqHNkb3SwPJCs8RIzD86s+WPpt2S73+EHCGO+NUA==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.16.7", - "@babel/plugin-syntax-export-namespace-from": "^7.8.3" - } - }, - "@babel/plugin-proposal-json-strings": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-json-strings/-/plugin-proposal-json-strings-7.16.7.tgz", - "integrity": "sha512-lNZ3EEggsGY78JavgbHsK9u5P3pQaW7k4axlgFLYkMd7UBsiNahCITShLjNQschPyjtO6dADrL24757IdhBrsQ==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.16.7", - "@babel/plugin-syntax-json-strings": "^7.8.3" - } - }, - "@babel/plugin-proposal-logical-assignment-operators": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-logical-assignment-operators/-/plugin-proposal-logical-assignment-operators-7.16.7.tgz", - "integrity": "sha512-K3XzyZJGQCr00+EtYtrDjmwX7o7PLK6U9bi1nCwkQioRFVUv6dJoxbQjtWVtP+bCPy82bONBKG8NPyQ4+i6yjg==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.16.7", - "@babel/plugin-syntax-logical-assignment-operators": "^7.10.4" - } - }, - "@babel/plugin-proposal-nullish-coalescing-operator": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-nullish-coalescing-operator/-/plugin-proposal-nullish-coalescing-operator-7.16.7.tgz", - "integrity": "sha512-aUOrYU3EVtjf62jQrCj63pYZ7k6vns2h/DQvHPWGmsJRYzWXZ6/AsfgpiRy6XiuIDADhJzP2Q9MwSMKauBQ+UQ==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.16.7", - "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3" - } - }, - "@babel/plugin-proposal-numeric-separator": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-numeric-separator/-/plugin-proposal-numeric-separator-7.16.7.tgz", - "integrity": "sha512-vQgPMknOIgiuVqbokToyXbkY/OmmjAzr/0lhSIbG/KmnzXPGwW/AdhdKpi+O4X/VkWiWjnkKOBiqJrTaC98VKw==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.16.7", - "@babel/plugin-syntax-numeric-separator": "^7.10.4" - } - }, - "@babel/plugin-proposal-object-rest-spread": { - "version": "7.17.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-object-rest-spread/-/plugin-proposal-object-rest-spread-7.17.3.tgz", - "integrity": "sha512-yuL5iQA/TbZn+RGAfxQXfi7CNLmKi1f8zInn4IgobuCWcAb7i+zj4TYzQ9l8cEzVyJ89PDGuqxK1xZpUDISesw==", - "dev": true, - "requires": { - "@babel/compat-data": "^7.17.0", - "@babel/helper-compilation-targets": "^7.16.7", - "@babel/helper-plugin-utils": "^7.16.7", - "@babel/plugin-syntax-object-rest-spread": "^7.8.3", - "@babel/plugin-transform-parameters": "^7.16.7" - } - }, - "@babel/plugin-proposal-optional-catch-binding": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-optional-catch-binding/-/plugin-proposal-optional-catch-binding-7.16.7.tgz", - "integrity": "sha512-eMOH/L4OvWSZAE1VkHbr1vckLG1WUcHGJSLqqQwl2GaUqG6QjddvrOaTUMNYiv77H5IKPMZ9U9P7EaHwvAShfA==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.16.7", - "@babel/plugin-syntax-optional-catch-binding": "^7.8.3" - } - }, - "@babel/plugin-proposal-optional-chaining": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-optional-chaining/-/plugin-proposal-optional-chaining-7.16.7.tgz", - "integrity": "sha512-eC3xy+ZrUcBtP7x+sq62Q/HYd674pPTb/77XZMb5wbDPGWIdUbSr4Agr052+zaUPSb+gGRnjxXfKFvx5iMJ+DA==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.16.7", - "@babel/helper-skip-transparent-expression-wrappers": "^7.16.0", - "@babel/plugin-syntax-optional-chaining": "^7.8.3" - } - }, - "@babel/plugin-proposal-private-methods": { - "version": "7.16.11", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-private-methods/-/plugin-proposal-private-methods-7.16.11.tgz", - "integrity": "sha512-F/2uAkPlXDr8+BHpZvo19w3hLFKge+k75XUprE6jaqKxjGkSYcK+4c+bup5PdW/7W/Rpjwql7FTVEDW+fRAQsw==", - "dev": true, - "requires": { - "@babel/helper-create-class-features-plugin": "^7.16.10", - "@babel/helper-plugin-utils": "^7.16.7" - } - }, - "@babel/plugin-proposal-private-property-in-object": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-private-property-in-object/-/plugin-proposal-private-property-in-object-7.16.7.tgz", - "integrity": "sha512-rMQkjcOFbm+ufe3bTZLyOfsOUOxyvLXZJCTARhJr+8UMSoZmqTe1K1BgkFcrW37rAchWg57yI69ORxiWvUINuQ==", - "dev": true, - "requires": { - "@babel/helper-annotate-as-pure": "^7.16.7", - "@babel/helper-create-class-features-plugin": "^7.16.7", - "@babel/helper-plugin-utils": "^7.16.7", - "@babel/plugin-syntax-private-property-in-object": "^7.14.5" - } - }, - "@babel/plugin-proposal-unicode-property-regex": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-unicode-property-regex/-/plugin-proposal-unicode-property-regex-7.16.7.tgz", - "integrity": "sha512-QRK0YI/40VLhNVGIjRNAAQkEHws0cswSdFFjpFyt943YmJIU1da9uW63Iu6NFV6CxTZW5eTDCrwZUstBWgp/Rg==", - "dev": true, - "requires": { - "@babel/helper-create-regexp-features-plugin": "^7.16.7", - "@babel/helper-plugin-utils": "^7.16.7" - } - }, - "@babel/plugin-syntax-async-generators": { - "version": "7.8.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.8.4.tgz", - "integrity": "sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.8.0" - } - }, - "@babel/plugin-syntax-class-properties": { - "version": "7.12.13", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-properties/-/plugin-syntax-class-properties-7.12.13.tgz", - "integrity": "sha512-fm4idjKla0YahUNgFNLCB0qySdsoPiZP3iQE3rky0mBUtMZ23yDJ9SJdg6dXTSDnulOVqiF3Hgr9nbXvXTQZYA==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.12.13" - } - }, - "@babel/plugin-syntax-class-static-block": { - "version": "7.14.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-static-block/-/plugin-syntax-class-static-block-7.14.5.tgz", - "integrity": "sha512-b+YyPmr6ldyNnM6sqYeMWE+bgJcJpO6yS4QD7ymxgH34GBPNDM/THBh8iunyvKIZztiwLH4CJZ0RxTk9emgpjw==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.14.5" - } - }, - "@babel/plugin-syntax-decorators": { - "version": "7.17.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-decorators/-/plugin-syntax-decorators-7.17.0.tgz", - "integrity": "sha512-qWe85yCXsvDEluNP0OyeQjH63DlhAR3W7K9BxxU1MvbDb48tgBG+Ao6IJJ6smPDrrVzSQZrbF6donpkFBMcs3A==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.16.7" - } - }, - "@babel/plugin-syntax-dynamic-import": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-dynamic-import/-/plugin-syntax-dynamic-import-7.8.3.tgz", - "integrity": "sha512-5gdGbFon+PszYzqs83S3E5mpi7/y/8M9eC90MRTZfduQOYW76ig6SOSPNe41IG5LoP3FGBn2N0RjVDSQiS94kQ==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.8.0" - } - }, - "@babel/plugin-syntax-export-namespace-from": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-export-namespace-from/-/plugin-syntax-export-namespace-from-7.8.3.tgz", - "integrity": "sha512-MXf5laXo6c1IbEbegDmzGPwGNTsHZmEy6QGznu5Sh2UCWvueywb2ee+CCE4zQiZstxU9BMoQO9i6zUFSY0Kj0Q==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.8.3" - } - }, - "@babel/plugin-syntax-json-strings": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-json-strings/-/plugin-syntax-json-strings-7.8.3.tgz", - "integrity": "sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.8.0" - } - }, - "@babel/plugin-syntax-jsx": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.16.7.tgz", - "integrity": "sha512-Esxmk7YjA8QysKeT3VhTXvF6y77f/a91SIs4pWb4H2eWGQkCKFgQaG6hdoEVZtGsrAcb2K5BW66XsOErD4WU3Q==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.16.7" - } - }, - "@babel/plugin-syntax-logical-assignment-operators": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-logical-assignment-operators/-/plugin-syntax-logical-assignment-operators-7.10.4.tgz", - "integrity": "sha512-d8waShlpFDinQ5MtvGU9xDAOzKH47+FFoney2baFIoMr952hKOLp1HR7VszoZvOsV/4+RRszNY7D17ba0te0ig==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.10.4" - } - }, - "@babel/plugin-syntax-nullish-coalescing-operator": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-nullish-coalescing-operator/-/plugin-syntax-nullish-coalescing-operator-7.8.3.tgz", - "integrity": "sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.8.0" - } - }, - "@babel/plugin-syntax-numeric-separator": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-numeric-separator/-/plugin-syntax-numeric-separator-7.10.4.tgz", - "integrity": "sha512-9H6YdfkcK/uOnY/K7/aA2xpzaAgkQn37yzWUMRK7OaPOqOpGS1+n0H5hxT9AUw9EsSjPW8SVyMJwYRtWs3X3ug==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.10.4" - } - }, - "@babel/plugin-syntax-object-rest-spread": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.8.3.tgz", - "integrity": "sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.8.0" - } - }, - "@babel/plugin-syntax-optional-catch-binding": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-catch-binding/-/plugin-syntax-optional-catch-binding-7.8.3.tgz", - "integrity": "sha512-6VPD0Pc1lpTqw0aKoeRTMiB+kWhAoT24PA+ksWSBrFtl5SIRVpZlwN3NNPQjehA2E/91FV3RjLWoVTglWcSV3Q==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.8.0" - } - }, - "@babel/plugin-syntax-optional-chaining": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-chaining/-/plugin-syntax-optional-chaining-7.8.3.tgz", - "integrity": "sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.8.0" - } - }, - "@babel/plugin-syntax-private-property-in-object": { - "version": "7.14.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-private-property-in-object/-/plugin-syntax-private-property-in-object-7.14.5.tgz", - "integrity": "sha512-0wVnp9dxJ72ZUJDV27ZfbSj6iHLoytYZmh3rFcxNnvsJF3ktkzLDZPy/mA17HGsaQT3/DQsWYX1f1QGWkCoVUg==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.14.5" - } - }, - "@babel/plugin-syntax-top-level-await": { - "version": "7.14.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-top-level-await/-/plugin-syntax-top-level-await-7.14.5.tgz", - "integrity": "sha512-hx++upLv5U1rgYfwe1xBQUhRmU41NEvpUvrp8jkrSCdvGSnM5/qdRMtylJ6PG5OFkBaHkbTAKTnd3/YyESRHFw==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.14.5" - } - }, - "@babel/plugin-transform-arrow-functions": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.16.7.tgz", - "integrity": "sha512-9ffkFFMbvzTvv+7dTp/66xvZAWASuPD5Tl9LK3Z9vhOmANo6j94rik+5YMBt4CwHVMWLWpMsriIc2zsa3WW3xQ==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.16.7" - } - }, - "@babel/plugin-transform-async-to-generator": { - "version": "7.16.8", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.16.8.tgz", - "integrity": "sha512-MtmUmTJQHCnyJVrScNzNlofQJ3dLFuobYn3mwOTKHnSCMtbNsqvF71GQmJfFjdrXSsAA7iysFmYWw4bXZ20hOg==", - "dev": true, - "requires": { - "@babel/helper-module-imports": "^7.16.7", - "@babel/helper-plugin-utils": "^7.16.7", - "@babel/helper-remap-async-to-generator": "^7.16.8" - } - }, - "@babel/plugin-transform-block-scoped-functions": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.16.7.tgz", - "integrity": "sha512-JUuzlzmF40Z9cXyytcbZEZKckgrQzChbQJw/5PuEHYeqzCsvebDx0K0jWnIIVcmmDOAVctCgnYs0pMcrYj2zJg==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.16.7" - } - }, - "@babel/plugin-transform-block-scoping": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.16.7.tgz", - "integrity": "sha512-ObZev2nxVAYA4bhyusELdo9hb3H+A56bxH3FZMbEImZFiEDYVHXQSJ1hQKFlDnlt8G9bBrCZ5ZpURZUrV4G5qQ==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.16.7" - } - }, - "@babel/plugin-transform-classes": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-classes/-/plugin-transform-classes-7.16.7.tgz", - "integrity": "sha512-WY7og38SFAGYRe64BrjKf8OrE6ulEHtr5jEYaZMwox9KebgqPi67Zqz8K53EKk1fFEJgm96r32rkKZ3qA2nCWQ==", - "dev": true, - "requires": { - "@babel/helper-annotate-as-pure": "^7.16.7", - "@babel/helper-environment-visitor": "^7.16.7", - "@babel/helper-function-name": "^7.16.7", - "@babel/helper-optimise-call-expression": "^7.16.7", - "@babel/helper-plugin-utils": "^7.16.7", - "@babel/helper-replace-supers": "^7.16.7", - "@babel/helper-split-export-declaration": "^7.16.7", - "globals": "^11.1.0" - } - }, - "@babel/plugin-transform-computed-properties": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.16.7.tgz", - "integrity": "sha512-gN72G9bcmenVILj//sv1zLNaPyYcOzUho2lIJBMh/iakJ9ygCo/hEF9cpGb61SCMEDxbbyBoVQxrt+bWKu5KGw==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.16.7" - } - }, - "@babel/plugin-transform-destructuring": { - "version": "7.17.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.17.7.tgz", - "integrity": "sha512-XVh0r5yq9sLR4vZ6eVZe8FKfIcSgaTBxVBRSYokRj2qksf6QerYnTxz9/GTuKTH/n/HwLP7t6gtlybHetJ/6hQ==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.16.7" - } - }, - "@babel/plugin-transform-dotall-regex": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.16.7.tgz", - "integrity": "sha512-Lyttaao2SjZF6Pf4vk1dVKv8YypMpomAbygW+mU5cYP3S5cWTfCJjG8xV6CFdzGFlfWK81IjL9viiTvpb6G7gQ==", - "dev": true, - "requires": { - "@babel/helper-create-regexp-features-plugin": "^7.16.7", - "@babel/helper-plugin-utils": "^7.16.7" - } - }, - "@babel/plugin-transform-duplicate-keys": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-7.16.7.tgz", - "integrity": "sha512-03DvpbRfvWIXyK0/6QiR1KMTWeT6OcQ7tbhjrXyFS02kjuX/mu5Bvnh5SDSWHxyawit2g5aWhKwI86EE7GUnTw==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.16.7" - } - }, - "@babel/plugin-transform-exponentiation-operator": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.16.7.tgz", - "integrity": "sha512-8UYLSlyLgRixQvlYH3J2ekXFHDFLQutdy7FfFAMm3CPZ6q9wHCwnUyiXpQCe3gVVnQlHc5nsuiEVziteRNTXEA==", - "dev": true, - "requires": { - "@babel/helper-builder-binary-assignment-operator-visitor": "^7.16.7", - "@babel/helper-plugin-utils": "^7.16.7" - } - }, - "@babel/plugin-transform-for-of": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.16.7.tgz", - "integrity": "sha512-/QZm9W92Ptpw7sjI9Nx1mbcsWz33+l8kuMIQnDwgQBG5s3fAfQvkRjQ7NqXhtNcKOnPkdICmUHyCaWW06HCsqg==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.16.7" - } - }, - "@babel/plugin-transform-function-name": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.16.7.tgz", - "integrity": "sha512-SU/C68YVwTRxqWj5kgsbKINakGag0KTgq9f2iZEXdStoAbOzLHEBRYzImmA6yFo8YZhJVflvXmIHUO7GWHmxxA==", - "dev": true, - "requires": { - "@babel/helper-compilation-targets": "^7.16.7", - "@babel/helper-function-name": "^7.16.7", - "@babel/helper-plugin-utils": "^7.16.7" - } - }, - "@babel/plugin-transform-literals": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-literals/-/plugin-transform-literals-7.16.7.tgz", - "integrity": "sha512-6tH8RTpTWI0s2sV6uq3e/C9wPo4PTqqZps4uF0kzQ9/xPLFQtipynvmT1g/dOfEJ+0EQsHhkQ/zyRId8J2b8zQ==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.16.7" - } - }, - "@babel/plugin-transform-member-expression-literals": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.16.7.tgz", - "integrity": "sha512-mBruRMbktKQwbxaJof32LT9KLy2f3gH+27a5XSuXo6h7R3vqltl0PgZ80C8ZMKw98Bf8bqt6BEVi3svOh2PzMw==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.16.7" - } - }, - "@babel/plugin-transform-modules-amd": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.16.7.tgz", - "integrity": "sha512-KaaEtgBL7FKYwjJ/teH63oAmE3lP34N3kshz8mm4VMAw7U3PxjVwwUmxEFksbgsNUaO3wId9R2AVQYSEGRa2+g==", - "dev": true, - "requires": { - "@babel/helper-module-transforms": "^7.16.7", - "@babel/helper-plugin-utils": "^7.16.7", - "babel-plugin-dynamic-import-node": "^2.3.3" - } - }, - "@babel/plugin-transform-modules-commonjs": { - "version": "7.17.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.17.7.tgz", - "integrity": "sha512-ITPmR2V7MqioMJyrxUo2onHNC3e+MvfFiFIR0RP21d3PtlVb6sfzoxNKiphSZUOM9hEIdzCcZe83ieX3yoqjUA==", - "dev": true, - "requires": { - "@babel/helper-module-transforms": "^7.17.7", - "@babel/helper-plugin-utils": "^7.16.7", - "@babel/helper-simple-access": "^7.17.7", - "babel-plugin-dynamic-import-node": "^2.3.3" - } - }, - "@babel/plugin-transform-modules-systemjs": { - "version": "7.17.8", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.17.8.tgz", - "integrity": "sha512-39reIkMTUVagzgA5x88zDYXPCMT6lcaRKs1+S9K6NKBPErbgO/w/kP8GlNQTC87b412ZTlmNgr3k2JrWgHH+Bw==", - "dev": true, - "requires": { - "@babel/helper-hoist-variables": "^7.16.7", - "@babel/helper-module-transforms": "^7.17.7", - "@babel/helper-plugin-utils": "^7.16.7", - "@babel/helper-validator-identifier": "^7.16.7", - "babel-plugin-dynamic-import-node": "^2.3.3" - } - }, - "@babel/plugin-transform-modules-umd": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-7.16.7.tgz", - "integrity": "sha512-EMh7uolsC8O4xhudF2F6wedbSHm1HHZ0C6aJ7K67zcDNidMzVcxWdGr+htW9n21klm+bOn+Rx4CBsAntZd3rEQ==", - "dev": true, - "requires": { - "@babel/helper-module-transforms": "^7.16.7", - "@babel/helper-plugin-utils": "^7.16.7" - } - }, - "@babel/plugin-transform-named-capturing-groups-regex": { - "version": "7.16.8", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.16.8.tgz", - "integrity": "sha512-j3Jw+n5PvpmhRR+mrgIh04puSANCk/T/UA3m3P1MjJkhlK906+ApHhDIqBQDdOgL/r1UYpz4GNclTXxyZrYGSw==", - "dev": true, - "requires": { - "@babel/helper-create-regexp-features-plugin": "^7.16.7" - } - }, - "@babel/plugin-transform-new-target": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-new-target/-/plugin-transform-new-target-7.16.7.tgz", - "integrity": "sha512-xiLDzWNMfKoGOpc6t3U+etCE2yRnn3SM09BXqWPIZOBpL2gvVrBWUKnsJx0K/ADi5F5YC5f8APFfWrz25TdlGg==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.16.7" - } - }, - "@babel/plugin-transform-object-super": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.16.7.tgz", - "integrity": "sha512-14J1feiQVWaGvRxj2WjyMuXS2jsBkgB3MdSN5HuC2G5nRspa5RK9COcs82Pwy5BuGcjb+fYaUj94mYcOj7rCvw==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.16.7", - "@babel/helper-replace-supers": "^7.16.7" - } - }, - "@babel/plugin-transform-parameters": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.16.7.tgz", - "integrity": "sha512-AT3MufQ7zZEhU2hwOA11axBnExW0Lszu4RL/tAlUJBuNoRak+wehQW8h6KcXOcgjY42fHtDxswuMhMjFEuv/aw==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.16.7" - } - }, - "@babel/plugin-transform-property-literals": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.16.7.tgz", - "integrity": "sha512-z4FGr9NMGdoIl1RqavCqGG+ZuYjfZ/hkCIeuH6Do7tXmSm0ls11nYVSJqFEUOSJbDab5wC6lRE/w6YjVcr6Hqw==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.16.7" - } - }, - "@babel/plugin-transform-regenerator": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.16.7.tgz", - "integrity": "sha512-mF7jOgGYCkSJagJ6XCujSQg+6xC1M77/03K2oBmVJWoFGNUtnVJO4WHKJk3dnPC8HCcj4xBQP1Egm8DWh3Pb3Q==", - "dev": true, - "requires": { - "regenerator-transform": "^0.14.2" - } - }, - "@babel/plugin-transform-reserved-words": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-reserved-words/-/plugin-transform-reserved-words-7.16.7.tgz", - "integrity": "sha512-KQzzDnZ9hWQBjwi5lpY5v9shmm6IVG0U9pB18zvMu2i4H90xpT4gmqwPYsn8rObiadYe2M0gmgsiOIF5A/2rtg==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.16.7" - } - }, - "@babel/plugin-transform-runtime": { - "version": "7.17.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-runtime/-/plugin-transform-runtime-7.17.0.tgz", - "integrity": "sha512-fr7zPWnKXNc1xoHfrIU9mN/4XKX4VLZ45Q+oMhfsYIaHvg7mHgmhfOy/ckRWqDK7XF3QDigRpkh5DKq6+clE8A==", - "dev": true, - "requires": { - "@babel/helper-module-imports": "^7.16.7", - "@babel/helper-plugin-utils": "^7.16.7", - "babel-plugin-polyfill-corejs2": "^0.3.0", - "babel-plugin-polyfill-corejs3": "^0.5.0", - "babel-plugin-polyfill-regenerator": "^0.3.0", - "semver": "^6.3.0" - } - }, - "@babel/plugin-transform-shorthand-properties": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.16.7.tgz", - "integrity": "sha512-hah2+FEnoRoATdIb05IOXf+4GzXYTq75TVhIn1PewihbpyrNWUt2JbudKQOETWw6QpLe+AIUpJ5MVLYTQbeeUg==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.16.7" - } - }, - "@babel/plugin-transform-spread": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-spread/-/plugin-transform-spread-7.16.7.tgz", - "integrity": "sha512-+pjJpgAngb53L0iaA5gU/1MLXJIfXcYepLgXB3esVRf4fqmj8f2cxM3/FKaHsZms08hFQJkFccEWuIpm429TXg==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.16.7", - "@babel/helper-skip-transparent-expression-wrappers": "^7.16.0" - } - }, - "@babel/plugin-transform-sticky-regex": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.16.7.tgz", - "integrity": "sha512-NJa0Bd/87QV5NZZzTuZG5BPJjLYadeSZ9fO6oOUoL4iQx+9EEuw/eEM92SrsT19Yc2jgB1u1hsjqDtH02c3Drw==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.16.7" - } - }, - "@babel/plugin-transform-template-literals": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.16.7.tgz", - "integrity": "sha512-VwbkDDUeenlIjmfNeDX/V0aWrQH2QiVyJtwymVQSzItFDTpxfyJh3EVaQiS0rIN/CqbLGr0VcGmuwyTdZtdIsA==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.16.7" - } - }, - "@babel/plugin-transform-typeof-symbol": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.16.7.tgz", - "integrity": "sha512-p2rOixCKRJzpg9JB4gjnG4gjWkWa89ZoYUnl9snJ1cWIcTH/hvxZqfO+WjG6T8DRBpctEol5jw1O5rA8gkCokQ==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.16.7" - } - }, - "@babel/plugin-transform-unicode-escapes": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-escapes/-/plugin-transform-unicode-escapes-7.16.7.tgz", - "integrity": "sha512-TAV5IGahIz3yZ9/Hfv35TV2xEm+kaBDaZQCn2S/hG9/CZ0DktxJv9eKfPc7yYCvOYR4JGx1h8C+jcSOvgaaI/Q==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.16.7" - } - }, - "@babel/plugin-transform-unicode-regex": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.16.7.tgz", - "integrity": "sha512-oC5tYYKw56HO75KZVLQ+R/Nl3Hro9kf8iG0hXoaHP7tjAyCpvqBiSNe6vGrZni1Z6MggmUOC6A7VP7AVmw225Q==", - "dev": true, - "requires": { - "@babel/helper-create-regexp-features-plugin": "^7.16.7", - "@babel/helper-plugin-utils": "^7.16.7" - } - }, - "@babel/preset-env": { - "version": "7.16.11", - "resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.16.11.tgz", - "integrity": "sha512-qcmWG8R7ZW6WBRPZK//y+E3Cli151B20W1Rv7ln27vuPaXU/8TKms6jFdiJtF7UDTxcrb7mZd88tAeK9LjdT8g==", - "dev": true, - "requires": { - "@babel/compat-data": "^7.16.8", - "@babel/helper-compilation-targets": "^7.16.7", - "@babel/helper-plugin-utils": "^7.16.7", - "@babel/helper-validator-option": "^7.16.7", - "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": "^7.16.7", - "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": "^7.16.7", - "@babel/plugin-proposal-async-generator-functions": "^7.16.8", - "@babel/plugin-proposal-class-properties": "^7.16.7", - "@babel/plugin-proposal-class-static-block": "^7.16.7", - "@babel/plugin-proposal-dynamic-import": "^7.16.7", - "@babel/plugin-proposal-export-namespace-from": "^7.16.7", - "@babel/plugin-proposal-json-strings": "^7.16.7", - "@babel/plugin-proposal-logical-assignment-operators": "^7.16.7", - "@babel/plugin-proposal-nullish-coalescing-operator": "^7.16.7", - "@babel/plugin-proposal-numeric-separator": "^7.16.7", - "@babel/plugin-proposal-object-rest-spread": "^7.16.7", - "@babel/plugin-proposal-optional-catch-binding": "^7.16.7", - "@babel/plugin-proposal-optional-chaining": "^7.16.7", - "@babel/plugin-proposal-private-methods": "^7.16.11", - "@babel/plugin-proposal-private-property-in-object": "^7.16.7", - "@babel/plugin-proposal-unicode-property-regex": "^7.16.7", - "@babel/plugin-syntax-async-generators": "^7.8.4", - "@babel/plugin-syntax-class-properties": "^7.12.13", - "@babel/plugin-syntax-class-static-block": "^7.14.5", - "@babel/plugin-syntax-dynamic-import": "^7.8.3", - "@babel/plugin-syntax-export-namespace-from": "^7.8.3", - "@babel/plugin-syntax-json-strings": "^7.8.3", - "@babel/plugin-syntax-logical-assignment-operators": "^7.10.4", - "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3", - "@babel/plugin-syntax-numeric-separator": "^7.10.4", - "@babel/plugin-syntax-object-rest-spread": "^7.8.3", - "@babel/plugin-syntax-optional-catch-binding": "^7.8.3", - "@babel/plugin-syntax-optional-chaining": "^7.8.3", - "@babel/plugin-syntax-private-property-in-object": "^7.14.5", - "@babel/plugin-syntax-top-level-await": "^7.14.5", - "@babel/plugin-transform-arrow-functions": "^7.16.7", - "@babel/plugin-transform-async-to-generator": "^7.16.8", - "@babel/plugin-transform-block-scoped-functions": "^7.16.7", - "@babel/plugin-transform-block-scoping": "^7.16.7", - "@babel/plugin-transform-classes": "^7.16.7", - "@babel/plugin-transform-computed-properties": "^7.16.7", - "@babel/plugin-transform-destructuring": "^7.16.7", - "@babel/plugin-transform-dotall-regex": "^7.16.7", - "@babel/plugin-transform-duplicate-keys": "^7.16.7", - "@babel/plugin-transform-exponentiation-operator": "^7.16.7", - "@babel/plugin-transform-for-of": "^7.16.7", - "@babel/plugin-transform-function-name": "^7.16.7", - "@babel/plugin-transform-literals": "^7.16.7", - "@babel/plugin-transform-member-expression-literals": "^7.16.7", - "@babel/plugin-transform-modules-amd": "^7.16.7", - "@babel/plugin-transform-modules-commonjs": "^7.16.8", - "@babel/plugin-transform-modules-systemjs": "^7.16.7", - "@babel/plugin-transform-modules-umd": "^7.16.7", - "@babel/plugin-transform-named-capturing-groups-regex": "^7.16.8", - "@babel/plugin-transform-new-target": "^7.16.7", - "@babel/plugin-transform-object-super": "^7.16.7", - "@babel/plugin-transform-parameters": "^7.16.7", - "@babel/plugin-transform-property-literals": "^7.16.7", - "@babel/plugin-transform-regenerator": "^7.16.7", - "@babel/plugin-transform-reserved-words": "^7.16.7", - "@babel/plugin-transform-shorthand-properties": "^7.16.7", - "@babel/plugin-transform-spread": "^7.16.7", - "@babel/plugin-transform-sticky-regex": "^7.16.7", - "@babel/plugin-transform-template-literals": "^7.16.7", - "@babel/plugin-transform-typeof-symbol": "^7.16.7", - "@babel/plugin-transform-unicode-escapes": "^7.16.7", - "@babel/plugin-transform-unicode-regex": "^7.16.7", - "@babel/preset-modules": "^0.1.5", - "@babel/types": "^7.16.8", - "babel-plugin-polyfill-corejs2": "^0.3.0", - "babel-plugin-polyfill-corejs3": "^0.5.0", - "babel-plugin-polyfill-regenerator": "^0.3.0", - "core-js-compat": "^3.20.2", - "semver": "^6.3.0" - } - }, - "@babel/preset-modules": { - "version": "0.1.5", - "resolved": "https://registry.npmjs.org/@babel/preset-modules/-/preset-modules-0.1.5.tgz", - "integrity": "sha512-A57th6YRG7oR3cq/yt/Y84MvGgE0eJG2F1JLhKuyG+jFxEgrd/HAMJatiFtmOiZurz+0DkrvbheCLaV5f2JfjA==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.0.0", - "@babel/plugin-proposal-unicode-property-regex": "^7.4.4", - "@babel/plugin-transform-dotall-regex": "^7.4.4", - "@babel/types": "^7.4.4", - "esutils": "^2.0.2" - } - }, - "@babel/runtime": { - "version": "7.17.8", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.17.8.tgz", - "integrity": "sha512-dQpEpK0O9o6lj6oPu0gRDbbnk+4LeHlNcBpspf6Olzt3GIX4P1lWF1gS+pHLDFlaJvbR6q7jCfQ08zA4QJBnmA==", - "dev": true, - "requires": { - "regenerator-runtime": "^0.13.4" - } - }, - "@babel/template": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.16.7.tgz", - "integrity": "sha512-I8j/x8kHUrbYRTUxXrrMbfCa7jxkE7tZre39x3kjr9hvI82cK1FfqLygotcWN5kdPGWcLdWMHpSBavse5tWw3w==", - "dev": true, - "requires": { - "@babel/code-frame": "^7.16.7", - "@babel/parser": "^7.16.7", - "@babel/types": "^7.16.7" - } - }, - "@babel/traverse": { - "version": "7.17.3", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.17.3.tgz", - "integrity": "sha512-5irClVky7TxRWIRtxlh2WPUUOLhcPN06AGgaQSB8AEwuyEBgJVuJ5imdHm5zxk8w0QS5T+tDfnDxAlhWjpb7cw==", - "dev": true, - "requires": { - "@babel/code-frame": "^7.16.7", - "@babel/generator": "^7.17.3", - "@babel/helper-environment-visitor": "^7.16.7", - "@babel/helper-function-name": "^7.16.7", - "@babel/helper-hoist-variables": "^7.16.7", - "@babel/helper-split-export-declaration": "^7.16.7", - "@babel/parser": "^7.17.3", - "@babel/types": "^7.17.0", - "debug": "^4.1.0", - "globals": "^11.1.0" - } - }, - "@babel/types": { - "version": "7.17.0", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.17.0.tgz", - "integrity": "sha512-TmKSNO4D5rzhL5bjWFcVHHLETzfQ/AmbKpKPOSjlP0WoHZ6L911fgoOKY4Alp/emzG4cHJdyN49zpgkbXFEHHw==", - "dev": true, - "requires": { - "@babel/helper-validator-identifier": "^7.16.7", - "to-fast-properties": "^2.0.0" - } - }, - "@fortawesome/fontawesome-common-types": { - "version": "0.2.36", - "resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-common-types/-/fontawesome-common-types-0.2.36.tgz", - "integrity": "sha512-a/7BiSgobHAgBWeN7N0w+lAhInrGxksn13uK7231n2m8EDPE3BMCl9NZLTGrj9ZXfCmC6LM0QLqXidIizVQ6yg==" - }, - "@fortawesome/fontawesome-svg-core": { - "version": "1.2.36", - "resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-svg-core/-/fontawesome-svg-core-1.2.36.tgz", - "integrity": "sha512-YUcsLQKYb6DmaJjIHdDWpBIGCcyE/W+p/LMGvjQem55Mm2XWVAP5kWTMKWLv9lwpCVjpLxPyOMOyUocP1GxrtA==", - "requires": { - "@fortawesome/fontawesome-common-types": "^0.2.36" - } - }, - "@fortawesome/free-solid-svg-icons": { - "version": "5.15.4", - "resolved": "https://registry.npmjs.org/@fortawesome/free-solid-svg-icons/-/free-solid-svg-icons-5.15.4.tgz", - "integrity": "sha512-JLmQfz6tdtwxoihXLg6lT78BorrFyCf59SAwBM6qV/0zXyVeDygJVb3fk+j5Qat+Yvcxp1buLTY5iDh1ZSAQ8w==", - "requires": { - "@fortawesome/fontawesome-common-types": "^0.2.36" - } - }, - "@fortawesome/vue-fontawesome": { - "version": "3.0.0-5", - "resolved": "https://registry.npmjs.org/@fortawesome/vue-fontawesome/-/vue-fontawesome-3.0.0-5.tgz", - "integrity": "sha512-aNmBT4bOecrFsZTog1l6AJDQHPP3ocXV+WQ3Ogy8WZCqstB/ahfhH4CPu5i4N9Hw0MBKXqE+LX+NbUxcj8cVTw==" - }, - "@hapi/hoek": { - "version": "9.2.1", - "resolved": "https://registry.npmjs.org/@hapi/hoek/-/hoek-9.2.1.tgz", - "integrity": "sha512-gfta+H8aziZsm8pZa0vj04KO6biEiisppNgA1kbJvFrrWu9Vm7eaUEy76DIxsuTaWvti5fkJVhllWc6ZTE+Mdw==", - "dev": true - }, - "@hapi/topo": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/@hapi/topo/-/topo-5.1.0.tgz", - "integrity": "sha512-foQZKJig7Ob0BMAYBfcJk8d77QtOe7Wo4ox7ff1lQYoNNAb6jwcY1ncdoy2e9wQZzvNy7ODZCYJkK8kzmcAnAg==", - "dev": true, - "requires": { - "@hapi/hoek": "^9.0.0" - } - }, - "@jridgewell/resolve-uri": { - "version": "3.0.5", - "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.0.5.tgz", - "integrity": "sha512-VPeQ7+wH0itvQxnG+lIzWgkysKIr3L9sslimFW55rHMdGu/qCQ5z5h9zq4gI8uBtqkpHhsF4Z/OwExufUCThew==", - "dev": true - }, - "@jridgewell/sourcemap-codec": { - "version": "1.4.11", - "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.11.tgz", - "integrity": "sha512-Fg32GrJo61m+VqYSdRSjRXMjQ06j8YIYfcTqndLYVAaHmroZHLJZCydsWBOTDqXS2v+mjxohBWEMfg97GXmYQg==", - "dev": true - }, - "@jridgewell/trace-mapping": { - "version": "0.3.4", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.4.tgz", - "integrity": "sha512-vFv9ttIedivx0ux3QSjhgtCVjPZd5l46ZOMDSCwnH1yUO2e964gO8LZGyv2QkqcgR6TnBU1v+1IFqmeoG+0UJQ==", - "dev": true, - "requires": { - "@jridgewell/resolve-uri": "^3.0.3", - "@jridgewell/sourcemap-codec": "^1.4.10" - } - }, - "@leichtgewicht/ip-codec": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/@leichtgewicht/ip-codec/-/ip-codec-2.0.3.tgz", - "integrity": "sha512-nkalE/f1RvRGChwBnEIoBfSEYOXnCRdleKuv6+lePbMDrMZXeDQnqak5XDOeBgrPPyPfAdcCu/B5z+v3VhplGg==", - "dev": true - }, - "@node-ipc/js-queue": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/@node-ipc/js-queue/-/js-queue-2.0.3.tgz", - "integrity": "sha512-fL1wpr8hhD5gT2dA1qifeVaoDFlQR5es8tFuKqjHX+kdOtdNHnxkVZbtIrR2rxnMFvehkjaZRNV2H/gPXlb0hw==", - "dev": true, - "requires": { - "easy-stack": "1.0.1" - } - }, - "@nodelib/fs.scandir": { - "version": "2.1.5", - "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", - "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", - "dev": true, - "requires": { - "@nodelib/fs.stat": "2.0.5", - "run-parallel": "^1.1.9" - } - }, - "@nodelib/fs.stat": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", - "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", - "dev": true - }, - "@nodelib/fs.walk": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", - "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", - "dev": true, - "requires": { - "@nodelib/fs.scandir": "2.1.5", - "fastq": "^1.6.0" - } - }, - "@oruga-ui/oruga-next": { - "version": "0.5.5", - "resolved": "https://registry.npmjs.org/@oruga-ui/oruga-next/-/oruga-next-0.5.5.tgz", - "integrity": "sha512-LmieX0fo0hbhJQRaKf4rfUlyDUwGVYkjGmSNfbontRKfKrEmmr1w1mxraF2Orxc894gkwi64NQh/SRSm4pIE5g==" - }, - "@polka/url": { - "version": "1.0.0-next.21", - "resolved": "https://registry.npmjs.org/@polka/url/-/url-1.0.0-next.21.tgz", - "integrity": "sha512-a5Sab1C4/icpTZVzZc5Ghpz88yQtGOyNqYXcZgOssB2uuAr+wF/MvN6bgtW32q7HHrvBki+BsZ0OuNv6EV3K9g==", - "dev": true - }, - "@sideway/address": { - "version": "4.1.4", - "resolved": "https://registry.npmjs.org/@sideway/address/-/address-4.1.4.tgz", - "integrity": "sha512-7vwq+rOHVWjyXxVlR76Agnvhy8I9rpzjosTESvmhNeXOXdZZB15Fl+TI9x1SiHZH5Jv2wTGduSxFDIaq0m3DUw==", - "dev": true, - "requires": { - "@hapi/hoek": "^9.0.0" - } - }, - "@sideway/formula": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@sideway/formula/-/formula-3.0.0.tgz", - "integrity": "sha512-vHe7wZ4NOXVfkoRb8T5otiENVlT7a3IAiw7H5M2+GO+9CDgcVUUsX1zalAztCmwyOr2RUTGJdgB+ZvSVqmdHmg==", - "dev": true - }, - "@sideway/pinpoint": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@sideway/pinpoint/-/pinpoint-2.0.0.tgz", - "integrity": "sha512-RNiOoTPkptFtSVzQevY/yWtZwf/RxyVnPy/OcA9HBM3MlGDnBEYL5B41H0MTn0Uec8Hi+2qUtTfG2WWZBmMejQ==", - "dev": true - }, - "@soda/friendly-errors-webpack-plugin": { - "version": "1.8.1", - "resolved": "https://registry.npmjs.org/@soda/friendly-errors-webpack-plugin/-/friendly-errors-webpack-plugin-1.8.1.tgz", - "integrity": "sha512-h2ooWqP8XuFqTXT+NyAFbrArzfQA7R6HTezADrvD9Re8fxMLTPPniLdqVTdDaO0eIoLaAwKT+d6w+5GeTk7Vbg==", - "dev": true, - "requires": { - "chalk": "^3.0.0", - "error-stack-parser": "^2.0.6", - "string-width": "^4.2.3", - "strip-ansi": "^6.0.1" - }, - "dependencies": { - "ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "requires": { - "color-convert": "^2.0.1" - } - }, - "chalk": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-3.0.0.tgz", - "integrity": "sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg==", - "dev": true, - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - } - }, - "color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "requires": { - "color-name": "~1.1.4" - } - }, - "color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true - }, - "supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "requires": { - "has-flag": "^4.0.0" - } - } - } - }, - "@soda/get-current-script": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/@soda/get-current-script/-/get-current-script-1.0.2.tgz", - "integrity": "sha512-T7VNNlYVM1SgQ+VsMYhnDkcGmWhQdL0bDyGm5TlQ3GBXnJscEClUUOKduWTmm2zCnvNLC1hc3JpuXjs/nFOc5w==", - "dev": true - }, - "@tauri-apps/api": { - "version": "1.0.0-rc.3", - "resolved": "https://registry.npmjs.org/@tauri-apps/api/-/api-1.0.0-rc.3.tgz", - "integrity": "sha512-/ZbyBjuUoU6mYm8RzibIUvVuJ31ZwYA2+fTRl7FF4LVFhzCVJ1YI1BS5ohGb2GsLNmLpfeo7kDHaIhV2uaOchQ==", - "requires": { - "type-fest": "2.12.1" - } - }, - "@tauri-apps/cli": { - "version": "1.0.0-rc.8", - "resolved": "https://registry.npmjs.org/@tauri-apps/cli/-/cli-1.0.0-rc.8.tgz", - "integrity": "sha512-FWpNwbgGMPgDb41oO7wdBhBsBE6FMG9yHKbtbrkJ/8d/hWcT6e3UUTfFL9cR2VGdp1pdWlul5+BKH0PTf8rB9w==", - "dev": true, - "requires": { - "@tauri-apps/cli-darwin-arm64": "1.0.0-rc.8", - "@tauri-apps/cli-darwin-x64": "1.0.0-rc.8", - "@tauri-apps/cli-linux-arm-gnueabihf": "1.0.0-rc.8", - "@tauri-apps/cli-linux-arm64-gnu": "1.0.0-rc.8", - "@tauri-apps/cli-linux-arm64-musl": "1.0.0-rc.8", - "@tauri-apps/cli-linux-x64-gnu": "1.0.0-rc.8", - "@tauri-apps/cli-linux-x64-musl": "1.0.0-rc.8", - "@tauri-apps/cli-win32-ia32-msvc": "1.0.0-rc.8", - "@tauri-apps/cli-win32-x64-msvc": "1.0.0-rc.8" - } - }, - "@tauri-apps/cli-darwin-arm64": { - "version": "1.0.0-rc.8", - "resolved": "https://registry.npmjs.org/@tauri-apps/cli-darwin-arm64/-/cli-darwin-arm64-1.0.0-rc.8.tgz", - "integrity": "sha512-sbV4fkd3YBlX0gEBldEVmN/NbBdHzrn8wCRRiFaNBocDWC6Ji6DNvpp1Vy1xgY0RgtGOisGD+3+HelmKP4Mh/A==", - "dev": true, - "optional": true - }, - "@tauri-apps/cli-darwin-x64": { - "version": "1.0.0-rc.8", - "resolved": "https://registry.npmjs.org/@tauri-apps/cli-darwin-x64/-/cli-darwin-x64-1.0.0-rc.8.tgz", - "integrity": "sha512-VmuYbBZKKal/YxSTbzHTaM04C9zUlTXq7ePoXc1PRAa/Xafo16N37OGzhzBlsKHrvh6BtPp9ZWk5UhHIDiJBsw==", - "dev": true, - "optional": true - }, - "@tauri-apps/cli-linux-arm-gnueabihf": { - "version": "1.0.0-rc.8", - "resolved": "https://registry.npmjs.org/@tauri-apps/cli-linux-arm-gnueabihf/-/cli-linux-arm-gnueabihf-1.0.0-rc.8.tgz", - "integrity": "sha512-2VKkS1c2pK56sgtGxtsm9nKKWefG7dgPnynTuCoEngkgQfNIlZ1ns/eAIZpBmwhvOMOw2OesKvK9ZQo0/lE/IA==", - "dev": true, - "optional": true - }, - "@tauri-apps/cli-linux-arm64-gnu": { - "version": "1.0.0-rc.8", - "resolved": "https://registry.npmjs.org/@tauri-apps/cli-linux-arm64-gnu/-/cli-linux-arm64-gnu-1.0.0-rc.8.tgz", - "integrity": "sha512-jykbrTJyx0uJhhL/3PPYGx5ETqEzacJ3YChnHzVClSzPkrdoGC61/xY9dMD9ZFJsLMJkV4VJj8sGRd1OPKCa/w==", - "dev": true, - "optional": true - }, - "@tauri-apps/cli-linux-arm64-musl": { - "version": "1.0.0-rc.8", - "resolved": "https://registry.npmjs.org/@tauri-apps/cli-linux-arm64-musl/-/cli-linux-arm64-musl-1.0.0-rc.8.tgz", - "integrity": "sha512-HAIm8BKpLIWkvYgxoRzDIaqWIW3UtH2HO1qDd0aov5FTx/9dtCTtylpvd/6mWs5LGaEmsLoSokfJloI2sMZ/WQ==", - "dev": true, - "optional": true - }, - "@tauri-apps/cli-linux-x64-gnu": { - "version": "1.0.0-rc.8", - "resolved": "https://registry.npmjs.org/@tauri-apps/cli-linux-x64-gnu/-/cli-linux-x64-gnu-1.0.0-rc.8.tgz", - "integrity": "sha512-fnPrkX6Uy9Qz2vqnT3D3sgbJzXTR4arNUIQl8HIYi2YCZZVmDWczz4x4++ilZWwk0I3a7d612/WzTeBzQjn3kg==", - "dev": true, - "optional": true - }, - "@tauri-apps/cli-linux-x64-musl": { - "version": "1.0.0-rc.8", - "resolved": "https://registry.npmjs.org/@tauri-apps/cli-linux-x64-musl/-/cli-linux-x64-musl-1.0.0-rc.8.tgz", - "integrity": "sha512-kHrpBsJFRAwqFL7xnsrKdNxnYjzI/J9/Dr7T3rl+IIPbi1sIojh6DydxFOYepnuoINA6zWYQJpo0FQeASJsXrw==", - "dev": true, - "optional": true - }, - "@tauri-apps/cli-win32-ia32-msvc": { - "version": "1.0.0-rc.8", - "resolved": "https://registry.npmjs.org/@tauri-apps/cli-win32-ia32-msvc/-/cli-win32-ia32-msvc-1.0.0-rc.8.tgz", - "integrity": "sha512-y8uT7AaIZVdLAWL4pxj/GXitwWn828er23yniOvTo1mJMCZsSFdwiOGiRSXI6OZsVCOPiJmvqq7eWaskQKYA0A==", - "dev": true, - "optional": true - }, - "@tauri-apps/cli-win32-x64-msvc": { - "version": "1.0.0-rc.8", - "resolved": "https://registry.npmjs.org/@tauri-apps/cli-win32-x64-msvc/-/cli-win32-x64-msvc-1.0.0-rc.8.tgz", - "integrity": "sha512-PiDr/iAKoL9nQSiWHkUSjRVHbvCJaa7Xj3P9UnGkjJQ9LGTr7AYZdLk4zISegF+/fZnOmaqUdZW9yWPPyJAzPw==", - "dev": true, - "optional": true - }, - "@trysound/sax": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/@trysound/sax/-/sax-0.2.0.tgz", - "integrity": "sha512-L7z9BgrNEcYyUYtF+HaEfiS5ebkh9jXqbszz7pC0hRBPaatV0XjSD3+eHrpqFemQfgwiFF0QPIarnIihIDn7OA==", - "dev": true - }, - "@types/body-parser": { - "version": "1.19.2", - "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.2.tgz", - "integrity": "sha512-ALYone6pm6QmwZoAgeyNksccT9Q4AWZQ6PvfwR37GT6r6FWUPguq6sUmNGSMV2Wr761oQoBxwGGa6DR5o1DC9g==", - "dev": true, - "requires": { - "@types/connect": "*", - "@types/node": "*" - } - }, - "@types/bonjour": { - "version": "3.5.10", - "resolved": "https://registry.npmjs.org/@types/bonjour/-/bonjour-3.5.10.tgz", - "integrity": "sha512-p7ienRMiS41Nu2/igbJxxLDWrSZ0WxM8UQgCeO9KhoVF7cOVFkrKsiDr1EsJIla8vV3oEEjGcz11jc5yimhzZw==", - "dev": true, - "requires": { - "@types/node": "*" - } - }, - "@types/connect": { - "version": "3.4.35", - "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.35.tgz", - "integrity": "sha512-cdeYyv4KWoEgpBISTxWvqYsVy444DOqehiF3fM3ne10AmJ62RSyNkUnxMJXHQWRQQX2eR94m5y1IZyDwBjV9FQ==", - "dev": true, - "requires": { - "@types/node": "*" - } - }, - "@types/connect-history-api-fallback": { - "version": "1.3.5", - "resolved": "https://registry.npmjs.org/@types/connect-history-api-fallback/-/connect-history-api-fallback-1.3.5.tgz", - "integrity": "sha512-h8QJa8xSb1WD4fpKBDcATDNGXghFj6/3GRWG6dhmRcu0RX1Ubasur2Uvx5aeEwlf0MwblEC2bMzzMQntxnw/Cw==", - "dev": true, - "requires": { - "@types/express-serve-static-core": "*", - "@types/node": "*" - } - }, - "@types/eslint": { - "version": "8.4.1", - "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-8.4.1.tgz", - "integrity": "sha512-GE44+DNEyxxh2Kc6ro/VkIj+9ma0pO0bwv9+uHSyBrikYOHr8zYcdPvnBOp1aw8s+CjRvuSx7CyWqRrNFQ59mA==", - "dev": true, - "requires": { - "@types/estree": "*", - "@types/json-schema": "*" - } - }, - "@types/eslint-scope": { - "version": "3.7.3", - "resolved": "https://registry.npmjs.org/@types/eslint-scope/-/eslint-scope-3.7.3.tgz", - "integrity": "sha512-PB3ldyrcnAicT35TWPs5IcwKD8S333HMaa2VVv4+wdvebJkjWuW/xESoB8IwRcog8HYVYamb1g/R31Qv5Bx03g==", - "dev": true, - "requires": { - "@types/eslint": "*", - "@types/estree": "*" - } - }, - "@types/estree": { - "version": "0.0.51", - "resolved": "https://registry.npmjs.org/@types/estree/-/estree-0.0.51.tgz", - "integrity": "sha512-CuPgU6f3eT/XgKKPqKd/gLZV1Xmvf1a2R5POBOGQa6uv82xpls89HU5zKeVoyR8XzHd1RGNOlQlvUe3CFkjWNQ==", - "dev": true - }, - "@types/express": { - "version": "4.17.13", - "resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.13.tgz", - "integrity": "sha512-6bSZTPaTIACxn48l50SR+axgrqm6qXFIxrdAKaG6PaJk3+zuUr35hBlgT7vOmJcum+OEaIBLtHV/qloEAFITeA==", - "dev": true, - "requires": { - "@types/body-parser": "*", - "@types/express-serve-static-core": "^4.17.18", - "@types/qs": "*", - "@types/serve-static": "*" - } - }, - "@types/express-serve-static-core": { - "version": "4.17.28", - "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.17.28.tgz", - "integrity": "sha512-P1BJAEAW3E2DJUlkgq4tOL3RyMunoWXqbSCygWo5ZIWTjUgN1YnaXWW4VWl/oc8vs/XoYibEGBKP0uZyF4AHig==", - "dev": true, - "requires": { - "@types/node": "*", - "@types/qs": "*", - "@types/range-parser": "*" - } - }, - "@types/html-minifier-terser": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/@types/html-minifier-terser/-/html-minifier-terser-6.1.0.tgz", - "integrity": "sha512-oh/6byDPnL1zeNXFrDXFLyZjkr1MsBG667IM792caf1L2UPOOMf65NFzjUH/ltyfwjAGfs1rsX1eftK0jC/KIg==", - "dev": true - }, - "@types/http-proxy": { - "version": "1.17.8", - "resolved": "https://registry.npmjs.org/@types/http-proxy/-/http-proxy-1.17.8.tgz", - "integrity": "sha512-5kPLG5BKpWYkw/LVOGWpiq3nEVqxiN32rTgI53Sk12/xHFQ2rG3ehI9IO+O3W2QoKeyB92dJkoka8SUm6BX1pA==", - "dev": true, - "requires": { - "@types/node": "*" - } - }, - "@types/json-schema": { - "version": "7.0.11", - "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.11.tgz", - "integrity": "sha512-wOuvG1SN4Us4rez+tylwwwCV1psiNVOkJeM3AUWUNWg/jDQY2+HE/444y5gc+jBmRqASOm2Oeh5c1axHobwRKQ==", - "dev": true - }, - "@types/mime": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/@types/mime/-/mime-1.3.2.tgz", - "integrity": "sha512-YATxVxgRqNH6nHEIsvg6k2Boc1JHI9ZbH5iWFFv/MTkchz3b1ieGDa5T0a9RznNdI0KhVbdbWSN+KWWrQZRxTw==", - "dev": true - }, - "@types/minimist": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/@types/minimist/-/minimist-1.2.2.tgz", - "integrity": "sha512-jhuKLIRrhvCPLqwPcx6INqmKeiA5EWrsCOPhrlFSrbrmU4ZMPjj5Ul/oLCMDO98XRUIwVm78xICz4EPCektzeQ==", - "dev": true - }, - "@types/node": { - "version": "17.0.23", - "resolved": "https://registry.npmjs.org/@types/node/-/node-17.0.23.tgz", - "integrity": "sha512-UxDxWn7dl97rKVeVS61vErvw086aCYhDLyvRQZ5Rk65rZKepaFdm53GeqXaKBuOhED4e9uWq34IC3TdSdJJ2Gw==", - "dev": true - }, - "@types/normalize-package-data": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/@types/normalize-package-data/-/normalize-package-data-2.4.1.tgz", - "integrity": "sha512-Gj7cI7z+98M282Tqmp2K5EIsoouUEzbBJhQQzDE3jSIRk6r9gsz0oUokqIUR4u1R3dMHo0pDHM7sNOHyhulypw==", - "dev": true - }, - "@types/parse-json": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/@types/parse-json/-/parse-json-4.0.0.tgz", - "integrity": "sha512-//oorEZjL6sbPcKUaCdIGlIUeH26mgzimjBB77G6XRgnDl/L5wOnpyBGRe/Mmf5CVW3PwEBE1NjiMZ/ssFh4wA==", - "dev": true - }, - "@types/qs": { - "version": "6.9.7", - "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.7.tgz", - "integrity": "sha512-FGa1F62FT09qcrueBA6qYTrJPVDzah9a+493+o2PCXsesWHIn27G98TsSMs3WPNbZIEj4+VJf6saSFpvD+3Zsw==", - "dev": true - }, - "@types/range-parser": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.4.tgz", - "integrity": "sha512-EEhsLsD6UsDM1yFhAvy0Cjr6VwmpMWqFBCb9w07wVugF7w9nfajxLuVmngTIpgS6svCnm6Vaw+MZhoDCKnOfsw==", - "dev": true - }, - "@types/retry": { - "version": "0.12.1", - "resolved": "https://registry.npmjs.org/@types/retry/-/retry-0.12.1.tgz", - "integrity": "sha512-xoDlM2S4ortawSWORYqsdU+2rxdh4LRW9ytc3zmT37RIKQh6IHyKwwtKhKis9ah8ol07DCkZxPt8BBvPjC6v4g==", - "dev": true - }, - "@types/serve-index": { - "version": "1.9.1", - "resolved": "https://registry.npmjs.org/@types/serve-index/-/serve-index-1.9.1.tgz", - "integrity": "sha512-d/Hs3nWDxNL2xAczmOVZNj92YZCS6RGxfBPjKzuu/XirCgXdpKEb88dYNbrYGint6IVWLNP+yonwVAuRC0T2Dg==", - "dev": true, - "requires": { - "@types/express": "*" - } - }, - "@types/serve-static": { - "version": "1.13.10", - "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.13.10.tgz", - "integrity": "sha512-nCkHGI4w7ZgAdNkrEu0bv+4xNV/XDqW+DydknebMOQwkpDGx8G+HTlj7R7ABI8i8nKxVw0wtKPi1D+lPOkh4YQ==", - "dev": true, - "requires": { - "@types/mime": "^1", - "@types/node": "*" - } - }, - "@types/sockjs": { - "version": "0.3.33", - "resolved": "https://registry.npmjs.org/@types/sockjs/-/sockjs-0.3.33.tgz", - "integrity": "sha512-f0KEEe05NvUnat+boPTZ0dgaLZ4SfSouXUgv5noUiefG2ajgKjmETo9ZJyuqsl7dfl2aHlLJUiki6B4ZYldiiw==", - "dev": true, - "requires": { - "@types/node": "*" - } - }, - "@types/ws": { - "version": "8.5.3", - "resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.5.3.tgz", - "integrity": "sha512-6YOoWjruKj1uLf3INHH7D3qTXwFfEsg1kf3c0uDdSBJwfa/llkwIjrAGV7j7mVgGNbzTQ3HiHKKDXl6bJPD97w==", - "dev": true, - "requires": { - "@types/node": "*" - } - }, - "@vue/babel-helper-vue-jsx-merge-props": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/@vue/babel-helper-vue-jsx-merge-props/-/babel-helper-vue-jsx-merge-props-1.2.1.tgz", - "integrity": "sha512-QOi5OW45e2R20VygMSNhyQHvpdUwQZqGPc748JLGCYEy+yp8fNFNdbNIGAgZmi9e+2JHPd6i6idRuqivyicIkA==", - "dev": true - }, - "@vue/babel-helper-vue-transform-on": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/@vue/babel-helper-vue-transform-on/-/babel-helper-vue-transform-on-1.0.2.tgz", - "integrity": "sha512-hz4R8tS5jMn8lDq6iD+yWL6XNB699pGIVLk7WSJnn1dbpjaazsjZQkieJoRX6gW5zpYSCFqQ7jUquPNY65tQYA==", - "dev": true - }, - "@vue/babel-plugin-jsx": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/@vue/babel-plugin-jsx/-/babel-plugin-jsx-1.1.1.tgz", - "integrity": "sha512-j2uVfZjnB5+zkcbc/zsOc0fSNGCMMjaEXP52wdwdIfn0qjFfEYpYZBFKFg+HHnQeJCVrjOeO0YxgaL7DMrym9w==", - "dev": true, - "requires": { - "@babel/helper-module-imports": "^7.0.0", - "@babel/plugin-syntax-jsx": "^7.0.0", - "@babel/template": "^7.0.0", - "@babel/traverse": "^7.0.0", - "@babel/types": "^7.0.0", - "@vue/babel-helper-vue-transform-on": "^1.0.2", - "camelcase": "^6.0.0", - "html-tags": "^3.1.0", - "svg-tags": "^1.0.0" - } - }, - "@vue/babel-plugin-transform-vue-jsx": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/@vue/babel-plugin-transform-vue-jsx/-/babel-plugin-transform-vue-jsx-1.2.1.tgz", - "integrity": "sha512-HJuqwACYehQwh1fNT8f4kyzqlNMpBuUK4rSiSES5D4QsYncv5fxFsLyrxFPG2ksO7t5WP+Vgix6tt6yKClwPzA==", - "dev": true, - "requires": { - "@babel/helper-module-imports": "^7.0.0", - "@babel/plugin-syntax-jsx": "^7.2.0", - "@vue/babel-helper-vue-jsx-merge-props": "^1.2.1", - "html-tags": "^2.0.0", - "lodash.kebabcase": "^4.1.1", - "svg-tags": "^1.0.0" - }, - "dependencies": { - "html-tags": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/html-tags/-/html-tags-2.0.0.tgz", - "integrity": "sha1-ELMKOGCF9Dzt41PMj6fLDe7qZos=", - "dev": true - } - } - }, - "@vue/babel-preset-app": { - "version": "5.0.4", - "resolved": "https://registry.npmjs.org/@vue/babel-preset-app/-/babel-preset-app-5.0.4.tgz", - "integrity": "sha512-vf4KqrmuOSnoEYGUiHPeMoxhh6wpiucLWXISn7xYFU80pK1lqcuhbl6tpurAanUIyRO/ENDUQBH7RAdbLNq1bA==", - "dev": true, - "requires": { - "@babel/core": "^7.12.16", - "@babel/helper-compilation-targets": "^7.12.16", - "@babel/helper-module-imports": "^7.12.13", - "@babel/plugin-proposal-class-properties": "^7.12.13", - "@babel/plugin-proposal-decorators": "^7.12.13", - "@babel/plugin-syntax-dynamic-import": "^7.8.3", - "@babel/plugin-syntax-jsx": "^7.12.13", - "@babel/plugin-transform-runtime": "^7.12.15", - "@babel/preset-env": "^7.12.16", - "@babel/runtime": "^7.12.13", - "@vue/babel-plugin-jsx": "^1.0.3", - "@vue/babel-preset-jsx": "^1.1.2", - "babel-plugin-dynamic-import-node": "^2.3.3", - "core-js": "^3.8.3", - "core-js-compat": "^3.8.3", - "semver": "^7.3.4" - }, - "dependencies": { - "semver": { - "version": "7.3.5", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz", - "integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==", - "dev": true, - "requires": { - "lru-cache": "^6.0.0" - } - } - } - }, - "@vue/babel-preset-jsx": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/@vue/babel-preset-jsx/-/babel-preset-jsx-1.2.4.tgz", - "integrity": "sha512-oRVnmN2a77bYDJzeGSt92AuHXbkIxbf/XXSE3klINnh9AXBmVS1DGa1f0d+dDYpLfsAKElMnqKTQfKn7obcL4w==", - "dev": true, - "requires": { - "@vue/babel-helper-vue-jsx-merge-props": "^1.2.1", - "@vue/babel-plugin-transform-vue-jsx": "^1.2.1", - "@vue/babel-sugar-composition-api-inject-h": "^1.2.1", - "@vue/babel-sugar-composition-api-render-instance": "^1.2.4", - "@vue/babel-sugar-functional-vue": "^1.2.2", - "@vue/babel-sugar-inject-h": "^1.2.2", - "@vue/babel-sugar-v-model": "^1.2.3", - "@vue/babel-sugar-v-on": "^1.2.3" - } - }, - "@vue/babel-sugar-composition-api-inject-h": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/@vue/babel-sugar-composition-api-inject-h/-/babel-sugar-composition-api-inject-h-1.2.1.tgz", - "integrity": "sha512-4B3L5Z2G+7s+9Bwbf+zPIifkFNcKth7fQwekVbnOA3cr3Pq71q71goWr97sk4/yyzH8phfe5ODVzEjX7HU7ItQ==", - "dev": true, - "requires": { - "@babel/plugin-syntax-jsx": "^7.2.0" - } - }, - "@vue/babel-sugar-composition-api-render-instance": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/@vue/babel-sugar-composition-api-render-instance/-/babel-sugar-composition-api-render-instance-1.2.4.tgz", - "integrity": "sha512-joha4PZznQMsxQYXtR3MnTgCASC9u3zt9KfBxIeuI5g2gscpTsSKRDzWQt4aqNIpx6cv8On7/m6zmmovlNsG7Q==", - "dev": true, - "requires": { - "@babel/plugin-syntax-jsx": "^7.2.0" - } - }, - "@vue/babel-sugar-functional-vue": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/@vue/babel-sugar-functional-vue/-/babel-sugar-functional-vue-1.2.2.tgz", - "integrity": "sha512-JvbgGn1bjCLByIAU1VOoepHQ1vFsroSA/QkzdiSs657V79q6OwEWLCQtQnEXD/rLTA8rRit4rMOhFpbjRFm82w==", - "dev": true, - "requires": { - "@babel/plugin-syntax-jsx": "^7.2.0" - } - }, - "@vue/babel-sugar-inject-h": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/@vue/babel-sugar-inject-h/-/babel-sugar-inject-h-1.2.2.tgz", - "integrity": "sha512-y8vTo00oRkzQTgufeotjCLPAvlhnpSkcHFEp60+LJUwygGcd5Chrpn5480AQp/thrxVm8m2ifAk0LyFel9oCnw==", - "dev": true, - "requires": { - "@babel/plugin-syntax-jsx": "^7.2.0" - } - }, - "@vue/babel-sugar-v-model": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/@vue/babel-sugar-v-model/-/babel-sugar-v-model-1.2.3.tgz", - "integrity": "sha512-A2jxx87mySr/ulAsSSyYE8un6SIH0NWHiLaCWpodPCVOlQVODCaSpiR4+IMsmBr73haG+oeCuSvMOM+ttWUqRQ==", - "dev": true, - "requires": { - "@babel/plugin-syntax-jsx": "^7.2.0", - "@vue/babel-helper-vue-jsx-merge-props": "^1.2.1", - "@vue/babel-plugin-transform-vue-jsx": "^1.2.1", - "camelcase": "^5.0.0", - "html-tags": "^2.0.0", - "svg-tags": "^1.0.0" - }, - "dependencies": { - "camelcase": { - "version": "5.3.1", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", - "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", - "dev": true - }, - "html-tags": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/html-tags/-/html-tags-2.0.0.tgz", - "integrity": "sha1-ELMKOGCF9Dzt41PMj6fLDe7qZos=", - "dev": true - } - } - }, - "@vue/babel-sugar-v-on": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/@vue/babel-sugar-v-on/-/babel-sugar-v-on-1.2.3.tgz", - "integrity": "sha512-kt12VJdz/37D3N3eglBywV8GStKNUhNrsxChXIV+o0MwVXORYuhDTHJRKPgLJRb/EY3vM2aRFQdxJBp9CLikjw==", - "dev": true, - "requires": { - "@babel/plugin-syntax-jsx": "^7.2.0", - "@vue/babel-plugin-transform-vue-jsx": "^1.2.1", - "camelcase": "^5.0.0" - }, - "dependencies": { - "camelcase": { - "version": "5.3.1", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", - "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", - "dev": true - } - } - }, - "@vue/cli-overlay": { - "version": "5.0.4", - "resolved": "https://registry.npmjs.org/@vue/cli-overlay/-/cli-overlay-5.0.4.tgz", - "integrity": "sha512-ZTLAAydILjvx0XHUGSNu/cQDpmvLTMYUutDf2vf6XGkSWYqncQ6RwkeMSQhvQNlgpa/ovwIgrlGxLoojFRwdVg==", - "dev": true - }, - "@vue/cli-plugin-babel": { - "version": "5.0.4", - "resolved": "https://registry.npmjs.org/@vue/cli-plugin-babel/-/cli-plugin-babel-5.0.4.tgz", - "integrity": "sha512-413ZwOWLtgw5vWJoMbrv36crW3qTas4Iru8sU7cRb0IqEZbS28R9X4PVtO8Pek2NYFbrs2XKRYOB7GblB6hVqg==", - "dev": true, - "requires": { - "@babel/core": "^7.12.16", - "@vue/babel-preset-app": "^5.0.4", - "@vue/cli-shared-utils": "^5.0.4", - "babel-loader": "^8.2.2", - "thread-loader": "^3.0.0", - "webpack": "^5.54.0" - } - }, - "@vue/cli-plugin-router": { - "version": "5.0.4", - "resolved": "https://registry.npmjs.org/@vue/cli-plugin-router/-/cli-plugin-router-5.0.4.tgz", - "integrity": "sha512-lylzCuH3Br0BcTz5IxxSffpyoF9dQ2k4jTdK8QlWrnRanWGw7P9C0kYMr9rohHaXpvAlu6bio392gbNIWpEepg==", - "dev": true, - "requires": { - "@vue/cli-shared-utils": "^5.0.4" - } - }, - "@vue/cli-plugin-vuex": { - "version": "5.0.4", - "resolved": "https://registry.npmjs.org/@vue/cli-plugin-vuex/-/cli-plugin-vuex-5.0.4.tgz", - "integrity": "sha512-dBwiD6mT9+V2HTHcwaWE8qFNgTk5I/NUvxYVeUN3Mmmpo4y/1RxXnr7BlKGnaQsTypb2RFk3KowqIJtg7s+E3Q==", - "dev": true - }, - "@vue/cli-service": { - "version": "5.0.4", - "resolved": "https://registry.npmjs.org/@vue/cli-service/-/cli-service-5.0.4.tgz", - "integrity": "sha512-xRiLNTFYmMCT9edZpyYXHijW5xot3gbZpcWDOXUOhKPHN4qs4XqWALnZlU97JYjZOr3XIr/ZvyciyEfrlUVqSA==", - "dev": true, - "requires": { - "@babel/helper-compilation-targets": "^7.12.16", - "@soda/friendly-errors-webpack-plugin": "^1.8.0", - "@soda/get-current-script": "^1.0.2", - "@types/minimist": "^1.2.0", - "@vue/cli-overlay": "^5.0.4", - "@vue/cli-plugin-router": "^5.0.4", - "@vue/cli-plugin-vuex": "^5.0.4", - "@vue/cli-shared-utils": "^5.0.4", - "@vue/component-compiler-utils": "^3.3.0", - "@vue/vue-loader-v15": "npm:vue-loader@^15.9.7", - "@vue/web-component-wrapper": "^1.3.0", - "acorn": "^8.0.5", - "acorn-walk": "^8.0.2", - "address": "^1.1.2", - "autoprefixer": "^10.2.4", - "browserslist": "^4.16.3", - "case-sensitive-paths-webpack-plugin": "^2.3.0", - "cli-highlight": "^2.1.10", - "clipboardy": "^2.3.0", - "cliui": "^7.0.4", - "copy-webpack-plugin": "^9.0.1", - "css-loader": "^6.5.0", - "css-minimizer-webpack-plugin": "^3.0.2", - "cssnano": "^5.0.0", - "debug": "^4.1.1", - "default-gateway": "^6.0.3", - "dotenv": "^10.0.0", - "dotenv-expand": "^5.1.0", - "fs-extra": "^9.1.0", - "globby": "^11.0.2", - "hash-sum": "^2.0.0", - "html-webpack-plugin": "^5.1.0", - "is-file-esm": "^1.0.0", - "launch-editor-middleware": "^2.2.1", - "lodash.defaultsdeep": "^4.6.1", - "lodash.mapvalues": "^4.6.0", - "mini-css-extract-plugin": "^2.5.3", - "minimist": "^1.2.5", - "module-alias": "^2.2.2", - "portfinder": "^1.0.26", - "postcss": "^8.2.6", - "postcss-loader": "^6.1.1", - "progress-webpack-plugin": "^1.0.12", - "ssri": "^8.0.1", - "terser-webpack-plugin": "^5.1.1", - "thread-loader": "^3.0.0", - "vue-loader": "^17.0.0", - "vue-style-loader": "^4.1.3", - "webpack": "^5.54.0", - "webpack-bundle-analyzer": "^4.4.0", - "webpack-chain": "^6.5.1", - "webpack-dev-server": "^4.7.3", - "webpack-merge": "^5.7.3", - "webpack-virtual-modules": "^0.4.2", - "whatwg-fetch": "^3.6.2" - } - }, - "@vue/cli-shared-utils": { - "version": "5.0.4", - "resolved": "https://registry.npmjs.org/@vue/cli-shared-utils/-/cli-shared-utils-5.0.4.tgz", - "integrity": "sha512-nfAsj8Nopu5sVHMBIaut/YL7NaJFVmTBSTJD7LM17jc5uytrM9JwiRtzCiv3JWRBG78Xdb/s2Xb/1YR4fkdmkQ==", - "dev": true, - "requires": { - "@achrinza/node-ipc": "9.2.2", - "chalk": "^4.1.2", - "execa": "^1.0.0", - "joi": "^17.4.0", - "launch-editor": "^2.2.1", - "lru-cache": "^6.0.0", - "node-fetch": "^2.6.7", - "open": "^8.0.2", - "ora": "^5.3.0", - "read-pkg": "^5.1.1", - "semver": "^7.3.4", - "strip-ansi": "^6.0.0" - }, - "dependencies": { - "ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "requires": { - "color-convert": "^2.0.1" - } - }, - "chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - } - }, - "color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "requires": { - "color-name": "~1.1.4" - } - }, - "color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true - }, - "semver": { - "version": "7.3.5", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz", - "integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==", - "dev": true, - "requires": { - "lru-cache": "^6.0.0" - } - }, - "supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "requires": { - "has-flag": "^4.0.0" - } - } - } - }, - "@vue/compiler-core": { - "version": "3.2.31", - "resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.2.31.tgz", - "integrity": "sha512-aKno00qoA4o+V/kR6i/pE+aP+esng5siNAVQ422TkBNM6qA4veXiZbSe8OTXHXquEi/f6Akc+nLfB4JGfe4/WQ==", - "requires": { - "@babel/parser": "^7.16.4", - "@vue/shared": "3.2.31", - "estree-walker": "^2.0.2", - "source-map": "^0.6.1" - } - }, - "@vue/compiler-dom": { - "version": "3.2.31", - "resolved": "https://registry.npmjs.org/@vue/compiler-dom/-/compiler-dom-3.2.31.tgz", - "integrity": "sha512-60zIlFfzIDf3u91cqfqy9KhCKIJgPeqxgveH2L+87RcGU/alT6BRrk5JtUso0OibH3O7NXuNOQ0cDc9beT0wrg==", - "requires": { - "@vue/compiler-core": "3.2.31", - "@vue/shared": "3.2.31" - } - }, - "@vue/compiler-sfc": { - "version": "3.2.31", - "resolved": "https://registry.npmjs.org/@vue/compiler-sfc/-/compiler-sfc-3.2.31.tgz", - "integrity": "sha512-748adc9msSPGzXgibHiO6T7RWgfnDcVQD+VVwYgSsyyY8Ans64tALHZANrKtOzvkwznV/F4H7OAod/jIlp/dkQ==", - "requires": { - "@babel/parser": "^7.16.4", - "@vue/compiler-core": "3.2.31", - "@vue/compiler-dom": "3.2.31", - "@vue/compiler-ssr": "3.2.31", - "@vue/reactivity-transform": "3.2.31", - "@vue/shared": "3.2.31", - "estree-walker": "^2.0.2", - "magic-string": "^0.25.7", - "postcss": "^8.1.10", - "source-map": "^0.6.1" - } - }, - "@vue/compiler-ssr": { - "version": "3.2.31", - "resolved": "https://registry.npmjs.org/@vue/compiler-ssr/-/compiler-ssr-3.2.31.tgz", - "integrity": "sha512-mjN0rqig+A8TVDnsGPYJM5dpbjlXeHUm2oZHZwGyMYiGT/F4fhJf/cXy8QpjnLQK4Y9Et4GWzHn9PS8AHUnSkw==", - "requires": { - "@vue/compiler-dom": "3.2.31", - "@vue/shared": "3.2.31" - } - }, - "@vue/component-compiler-utils": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/@vue/component-compiler-utils/-/component-compiler-utils-3.3.0.tgz", - "integrity": "sha512-97sfH2mYNU+2PzGrmK2haqffDpVASuib9/w2/noxiFi31Z54hW+q3izKQXXQZSNhtiUpAI36uSuYepeBe4wpHQ==", - "dev": true, - "requires": { - "consolidate": "^0.15.1", - "hash-sum": "^1.0.2", - "lru-cache": "^4.1.2", - "merge-source-map": "^1.1.0", - "postcss": "^7.0.36", - "postcss-selector-parser": "^6.0.2", - "prettier": "^1.18.2 || ^2.0.0", - "source-map": "~0.6.1", - "vue-template-es2015-compiler": "^1.9.0" - }, - "dependencies": { - "hash-sum": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/hash-sum/-/hash-sum-1.0.2.tgz", - "integrity": "sha1-M7QHd3VMZDJXPBIMw4CLvRDUfwQ=", - "dev": true - }, - "lru-cache": { - "version": "4.1.5", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-4.1.5.tgz", - "integrity": "sha512-sWZlbEP2OsHNkXrMl5GYk/jKk70MBng6UU4YI/qGDYbgf6YbP4EvmqISbXCoJiRKs+1bSpFHVgQxvJ17F2li5g==", - "dev": true, - "requires": { - "pseudomap": "^1.0.2", - "yallist": "^2.1.2" - } - }, - "picocolors": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-0.2.1.tgz", - "integrity": "sha512-cMlDqaLEqfSaW8Z7N5Jw+lyIW869EzT73/F5lhtY9cLGoVxSXznfgfXMO0Z5K0o0Q2TkTXq+0KFsdnSe3jDViA==", - "dev": true - }, - "postcss": { - "version": "7.0.39", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.39.tgz", - "integrity": "sha512-yioayjNbHn6z1/Bywyb2Y4s3yvDAeXGOyxqD+LnVOinq6Mdmd++SW2wUNVzavyyHxd6+DxzWGIuosg6P1Rj8uA==", - "dev": true, - "requires": { - "picocolors": "^0.2.1", - "source-map": "^0.6.1" - } - }, - "yallist": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-2.1.2.tgz", - "integrity": "sha1-HBH5IY8HYImkfdUS+TxmmaaoHVI=", - "dev": true - } - } - }, - "@vue/devtools-api": { - "version": "6.1.4", - "resolved": "https://registry.npmjs.org/@vue/devtools-api/-/devtools-api-6.1.4.tgz", - "integrity": "sha512-IiA0SvDrJEgXvVxjNkHPFfDx6SXw0b/TUkqMcDZWNg9fnCAHbTpoo59YfJ9QLFkwa3raau5vSlRVzMSLDnfdtQ==" - }, - "@vue/reactivity": { - "version": "3.2.31", - "resolved": "https://registry.npmjs.org/@vue/reactivity/-/reactivity-3.2.31.tgz", - "integrity": "sha512-HVr0l211gbhpEKYr2hYe7hRsV91uIVGFYNHj73njbARVGHQvIojkImKMaZNDdoDZOIkMsBc9a1sMqR+WZwfSCw==", - "requires": { - "@vue/shared": "3.2.31" - } - }, - "@vue/reactivity-transform": { - "version": "3.2.31", - "resolved": "https://registry.npmjs.org/@vue/reactivity-transform/-/reactivity-transform-3.2.31.tgz", - "integrity": "sha512-uS4l4z/W7wXdI+Va5pgVxBJ345wyGFKvpPYtdSgvfJfX/x2Ymm6ophQlXXB6acqGHtXuBqNyyO3zVp9b1r0MOA==", - "requires": { - "@babel/parser": "^7.16.4", - "@vue/compiler-core": "3.2.31", - "@vue/shared": "3.2.31", - "estree-walker": "^2.0.2", - "magic-string": "^0.25.7" - } - }, - "@vue/runtime-core": { - "version": "3.2.31", - "resolved": "https://registry.npmjs.org/@vue/runtime-core/-/runtime-core-3.2.31.tgz", - "integrity": "sha512-Kcog5XmSY7VHFEMuk4+Gap8gUssYMZ2+w+cmGI6OpZWYOEIcbE0TPzzPHi+8XTzAgx1w/ZxDFcXhZeXN5eKWsA==", - "requires": { - "@vue/reactivity": "3.2.31", - "@vue/shared": "3.2.31" - } - }, - "@vue/runtime-dom": { - "version": "3.2.31", - "resolved": "https://registry.npmjs.org/@vue/runtime-dom/-/runtime-dom-3.2.31.tgz", - "integrity": "sha512-N+o0sICVLScUjfLG7u9u5XCjvmsexAiPt17GNnaWHJUfsKed5e85/A3SWgKxzlxx2SW/Hw7RQxzxbXez9PtY3g==", - "requires": { - "@vue/runtime-core": "3.2.31", - "@vue/shared": "3.2.31", - "csstype": "^2.6.8" - } - }, - "@vue/server-renderer": { - "version": "3.2.31", - "resolved": "https://registry.npmjs.org/@vue/server-renderer/-/server-renderer-3.2.31.tgz", - "integrity": "sha512-8CN3Zj2HyR2LQQBHZ61HexF5NReqngLT3oahyiVRfSSvak+oAvVmu8iNLSu6XR77Ili2AOpnAt1y8ywjjqtmkg==", - "requires": { - "@vue/compiler-ssr": "3.2.31", - "@vue/shared": "3.2.31" - } - }, - "@vue/shared": { - "version": "3.2.31", - "resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.2.31.tgz", - "integrity": "sha512-ymN2pj6zEjiKJZbrf98UM2pfDd6F2H7ksKw7NDt/ZZ1fh5Ei39X5tABugtT03ZRlWd9imccoK0hE8hpjpU7irQ==" - }, - "@vue/vue-loader-v15": { - "version": "npm:vue-loader@15.9.8", - "resolved": "https://registry.npmjs.org/vue-loader/-/vue-loader-15.9.8.tgz", - "integrity": "sha512-GwSkxPrihfLR69/dSV3+5CdMQ0D+jXg8Ma1S4nQXKJAznYFX14vHdc/NetQc34Dw+rBbIJyP7JOuVb9Fhprvog==", - "dev": true, - "requires": { - "@vue/component-compiler-utils": "^3.1.0", - "hash-sum": "^1.0.2", - "loader-utils": "^1.1.0", - "vue-hot-reload-api": "^2.3.0", - "vue-style-loader": "^4.1.0" - }, - "dependencies": { - "hash-sum": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/hash-sum/-/hash-sum-1.0.2.tgz", - "integrity": "sha1-M7QHd3VMZDJXPBIMw4CLvRDUfwQ=", - "dev": true - }, - "json5": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.1.tgz", - "integrity": "sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow==", - "dev": true, - "requires": { - "minimist": "^1.2.0" - } - }, - "loader-utils": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.4.0.tgz", - "integrity": "sha512-qH0WSMBtn/oHuwjy/NucEgbx5dbxxnxup9s4PVXJUDHZBQY+s0NWA9rJf53RBnQZxfch7euUui7hpoAPvALZdA==", - "dev": true, - "requires": { - "big.js": "^5.2.2", - "emojis-list": "^3.0.0", - "json5": "^1.0.1" - } - } - } - }, - "@vue/web-component-wrapper": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/@vue/web-component-wrapper/-/web-component-wrapper-1.3.0.tgz", - "integrity": "sha512-Iu8Tbg3f+emIIMmI2ycSI8QcEuAUgPTgHwesDU1eKMLE4YC/c/sFbGc70QgMq31ijRftV0R7vCm9co6rldCeOA==", - "dev": true - }, - "@webassemblyjs/ast": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.11.1.tgz", - "integrity": "sha512-ukBh14qFLjxTQNTXocdyksN5QdM28S1CxHt2rdskFyL+xFV7VremuBLVbmCePj+URalXBENx/9Lm7lnhihtCSw==", - "dev": true, - "requires": { - "@webassemblyjs/helper-numbers": "1.11.1", - "@webassemblyjs/helper-wasm-bytecode": "1.11.1" - } - }, - "@webassemblyjs/floating-point-hex-parser": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.11.1.tgz", - "integrity": "sha512-iGRfyc5Bq+NnNuX8b5hwBrRjzf0ocrJPI6GWFodBFzmFnyvrQ83SHKhmilCU/8Jv67i4GJZBMhEzltxzcNagtQ==", - "dev": true - }, - "@webassemblyjs/helper-api-error": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-api-error/-/helper-api-error-1.11.1.tgz", - "integrity": "sha512-RlhS8CBCXfRUR/cwo2ho9bkheSXG0+NwooXcc3PAILALf2QLdFyj7KGsKRbVc95hZnhnERon4kW/D3SZpp6Tcg==", - "dev": true - }, - "@webassemblyjs/helper-buffer": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-buffer/-/helper-buffer-1.11.1.tgz", - "integrity": "sha512-gwikF65aDNeeXa8JxXa2BAk+REjSyhrNC9ZwdT0f8jc4dQQeDQ7G4m0f2QCLPJiMTTO6wfDmRmj/pW0PsUvIcA==", - "dev": true - }, - "@webassemblyjs/helper-numbers": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-numbers/-/helper-numbers-1.11.1.tgz", - "integrity": "sha512-vDkbxiB8zfnPdNK9Rajcey5C0w+QJugEglN0of+kmO8l7lDb77AnlKYQF7aarZuCrv+l0UvqL+68gSDr3k9LPQ==", - "dev": true, - "requires": { - "@webassemblyjs/floating-point-hex-parser": "1.11.1", - "@webassemblyjs/helper-api-error": "1.11.1", - "@xtuc/long": "4.2.2" - } - }, - "@webassemblyjs/helper-wasm-bytecode": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.11.1.tgz", - "integrity": "sha512-PvpoOGiJwXeTrSf/qfudJhwlvDQxFgelbMqtq52WWiXC6Xgg1IREdngmPN3bs4RoO83PnL/nFrxucXj1+BX62Q==", - "dev": true - }, - "@webassemblyjs/helper-wasm-section": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.11.1.tgz", - "integrity": "sha512-10P9No29rYX1j7F3EVPX3JvGPQPae+AomuSTPiF9eBQeChHI6iqjMIwR9JmOJXwpnn/oVGDk7I5IlskuMwU/pg==", - "dev": true, - "requires": { - "@webassemblyjs/ast": "1.11.1", - "@webassemblyjs/helper-buffer": "1.11.1", - "@webassemblyjs/helper-wasm-bytecode": "1.11.1", - "@webassemblyjs/wasm-gen": "1.11.1" - } - }, - "@webassemblyjs/ieee754": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/ieee754/-/ieee754-1.11.1.tgz", - "integrity": "sha512-hJ87QIPtAMKbFq6CGTkZYJivEwZDbQUgYd3qKSadTNOhVY7p+gfP6Sr0lLRVTaG1JjFj+r3YchoqRYxNH3M0GQ==", - "dev": true, - "requires": { - "@xtuc/ieee754": "^1.2.0" - } - }, - "@webassemblyjs/leb128": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/leb128/-/leb128-1.11.1.tgz", - "integrity": "sha512-BJ2P0hNZ0u+Th1YZXJpzW6miwqQUGcIHT1G/sf72gLVD9DZ5AdYTqPNbHZh6K1M5VmKvFXwGSWZADz+qBWxeRw==", - "dev": true, - "requires": { - "@xtuc/long": "4.2.2" - } - }, - "@webassemblyjs/utf8": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/utf8/-/utf8-1.11.1.tgz", - "integrity": "sha512-9kqcxAEdMhiwQkHpkNiorZzqpGrodQQ2IGrHHxCy+Ozng0ofyMA0lTqiLkVs1uzTRejX+/O0EOT7KxqVPuXosQ==", - "dev": true - }, - "@webassemblyjs/wasm-edit": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-edit/-/wasm-edit-1.11.1.tgz", - "integrity": "sha512-g+RsupUC1aTHfR8CDgnsVRVZFJqdkFHpsHMfJuWQzWU3tvnLC07UqHICfP+4XyL2tnr1amvl1Sdp06TnYCmVkA==", - "dev": true, - "requires": { - "@webassemblyjs/ast": "1.11.1", - "@webassemblyjs/helper-buffer": "1.11.1", - "@webassemblyjs/helper-wasm-bytecode": "1.11.1", - "@webassemblyjs/helper-wasm-section": "1.11.1", - "@webassemblyjs/wasm-gen": "1.11.1", - "@webassemblyjs/wasm-opt": "1.11.1", - "@webassemblyjs/wasm-parser": "1.11.1", - "@webassemblyjs/wast-printer": "1.11.1" - } - }, - "@webassemblyjs/wasm-gen": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-gen/-/wasm-gen-1.11.1.tgz", - "integrity": "sha512-F7QqKXwwNlMmsulj6+O7r4mmtAlCWfO/0HdgOxSklZfQcDu0TpLiD1mRt/zF25Bk59FIjEuGAIyn5ei4yMfLhA==", - "dev": true, - "requires": { - "@webassemblyjs/ast": "1.11.1", - "@webassemblyjs/helper-wasm-bytecode": "1.11.1", - "@webassemblyjs/ieee754": "1.11.1", - "@webassemblyjs/leb128": "1.11.1", - "@webassemblyjs/utf8": "1.11.1" - } - }, - "@webassemblyjs/wasm-opt": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-opt/-/wasm-opt-1.11.1.tgz", - "integrity": "sha512-VqnkNqnZlU5EB64pp1l7hdm3hmQw7Vgqa0KF/KCNO9sIpI6Fk6brDEiX+iCOYrvMuBWDws0NkTOxYEb85XQHHw==", - "dev": true, - "requires": { - "@webassemblyjs/ast": "1.11.1", - "@webassemblyjs/helper-buffer": "1.11.1", - "@webassemblyjs/wasm-gen": "1.11.1", - "@webassemblyjs/wasm-parser": "1.11.1" - } - }, - "@webassemblyjs/wasm-parser": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-parser/-/wasm-parser-1.11.1.tgz", - "integrity": "sha512-rrBujw+dJu32gYB7/Lup6UhdkPx9S9SnobZzRVL7VcBH9Bt9bCBLEuX/YXOOtBsOZ4NQrRykKhffRWHvigQvOA==", - "dev": true, - "requires": { - "@webassemblyjs/ast": "1.11.1", - "@webassemblyjs/helper-api-error": "1.11.1", - "@webassemblyjs/helper-wasm-bytecode": "1.11.1", - "@webassemblyjs/ieee754": "1.11.1", - "@webassemblyjs/leb128": "1.11.1", - "@webassemblyjs/utf8": "1.11.1" - } - }, - "@webassemblyjs/wast-printer": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wast-printer/-/wast-printer-1.11.1.tgz", - "integrity": "sha512-IQboUWM4eKzWW+N/jij2sRatKMh99QEelo3Eb2q0qXkvPRISAj8Qxtmw5itwqK+TTkBuUIE45AxYPToqPtL5gg==", - "dev": true, - "requires": { - "@webassemblyjs/ast": "1.11.1", - "@xtuc/long": "4.2.2" - } - }, - "@xtuc/ieee754": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/@xtuc/ieee754/-/ieee754-1.2.0.tgz", - "integrity": "sha512-DX8nKgqcGwsc0eJSqYt5lwP4DH5FlHnmuWWBRy7X0NcaGR0ZtuyeESgMwTYVEtxmsNGY+qit4QYT/MIYTOTPeA==", - "dev": true - }, - "@xtuc/long": { - "version": "4.2.2", - "resolved": "https://registry.npmjs.org/@xtuc/long/-/long-4.2.2.tgz", - "integrity": "sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ==", - "dev": true - }, - "CBuffer": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/CBuffer/-/CBuffer-2.2.0.tgz", - "integrity": "sha512-dbfGsL/kaEA07tNX4NdUPwToq/elfc+4IbDnq5INOl+LmYlaAqfTXqwnDCfvUeYtO78Ts2WhaCOUn3bSZgDwZA==" - }, - "accepts": { - "version": "1.3.8", - "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", - "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==", - "dev": true, - "requires": { - "mime-types": "~2.1.34", - "negotiator": "0.6.3" - } - }, - "acorn": { - "version": "8.7.0", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.7.0.tgz", - "integrity": "sha512-V/LGr1APy+PXIwKebEWrkZPwoeoF+w1jiOBUmuxuiUIaOHtob8Qc9BTrYo7VuI5fR8tqsy+buA2WFooR5olqvQ==", - "dev": true - }, - "acorn-import-assertions": { - "version": "1.8.0", - "resolved": "https://registry.npmjs.org/acorn-import-assertions/-/acorn-import-assertions-1.8.0.tgz", - "integrity": "sha512-m7VZ3jwz4eK6A4Vtt8Ew1/mNbP24u0FhdyfA7fSvnJR6LMdfOYnmuIrrJAgrYfYJ10F/otaHTtrtrtmHdMNzEw==", - "dev": true - }, - "acorn-walk": { - "version": "8.2.0", - "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.2.0.tgz", - "integrity": "sha512-k+iyHEuPgSw6SbuDpGQM+06HQUa04DZ3o+F6CSzXMvvI5KMvnaEqXe+YVe555R9nn6GPt404fos4wcgpw12SDA==", - "dev": true - }, - "address": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/address/-/address-1.1.2.tgz", - "integrity": "sha512-aT6camzM4xEA54YVJYSqxz1kv4IHnQZRtThJJHhUMRExaU5spC7jX5ugSwTaTgJliIgs4VhZOk7htClvQ/LmRA==", - "dev": true - }, - "ajv": { - "version": "6.12.6", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", - "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", - "dev": true, - "requires": { - "fast-deep-equal": "^3.1.1", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.4.1", - "uri-js": "^4.2.2" - } - }, - "ajv-formats": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-2.1.1.tgz", - "integrity": "sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA==", - "dev": true, - "requires": { - "ajv": "^8.0.0" - }, - "dependencies": { - "ajv": { - "version": "8.11.0", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.11.0.tgz", - "integrity": "sha512-wGgprdCvMalC0BztXvitD2hC04YffAvtsUn93JbGXYLAtCUO4xd17mCCZQxUOItiBwZvJScWo8NIvQMQ71rdpg==", - "dev": true, - "requires": { - "fast-deep-equal": "^3.1.1", - "json-schema-traverse": "^1.0.0", - "require-from-string": "^2.0.2", - "uri-js": "^4.2.2" - } - }, - "json-schema-traverse": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", - "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", - "dev": true - } - } - }, - "ajv-keywords": { - "version": "3.5.2", - "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz", - "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==", - "dev": true - }, - "ansi-escapes": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-3.2.0.tgz", - "integrity": "sha512-cBhpre4ma+U0T1oM5fXg7Dy1Jw7zzwv7lt/GoCpr+hDQJoYnKVPLL4dCvSEFMmQurOQvSrwT7SL/DAlhBI97RQ==", - "dev": true - }, - "ansi-html-community": { - "version": "0.0.8", - "resolved": "https://registry.npmjs.org/ansi-html-community/-/ansi-html-community-0.0.8.tgz", - "integrity": "sha512-1APHAyr3+PCamwNw3bXCPp4HFLONZt/yIH0sZp0/469KWNTEy+qN5jQ3GVX6DMZ1UXAi34yVwtTeaG/HpBuuzw==", - "dev": true - }, - "ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "dev": true - }, - "ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "dev": true, - "requires": { - "color-convert": "^1.9.0" - } - }, - "any-promise": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/any-promise/-/any-promise-1.3.0.tgz", - "integrity": "sha1-q8av7tzqUugJzcA3au0845Y10X8=", - "dev": true - }, - "anymatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.2.tgz", - "integrity": "sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg==", - "dev": true, - "requires": { - "normalize-path": "^3.0.0", - "picomatch": "^2.0.4" - } - }, - "arch": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/arch/-/arch-2.2.0.tgz", - "integrity": "sha512-Of/R0wqp83cgHozfIYLbBMnej79U/SVGOOyuB3VVFv1NRM/PSFMK12x9KVtiYzJqmnU5WR2qp0Z5rHb7sWGnFQ==", - "dev": true - }, - "array-flatten": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-2.1.2.tgz", - "integrity": "sha512-hNfzcOV8W4NdualtqBFPyVO+54DSJuZGY9qT4pRroB6S9e3iiido2ISIC5h9R2sPJ8H3FHCIiEnsv1lPXO3KtQ==", - "dev": true - }, - "array-union": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", - "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==", - "dev": true - }, - "async": { - "version": "2.6.4", - "resolved": "https://registry.npmjs.org/async/-/async-2.6.4.tgz", - "integrity": "sha512-mzo5dfJYwAn29PeiJ0zvwTo04zj8HDJj0Mn8TD7sno7q12prdbnasKJHhkm2c1LgrhlJ0teaea8860oxi51mGA==", - "dev": true, - "requires": { - "lodash": "^4.17.14" - } - }, - "at-least-node": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/at-least-node/-/at-least-node-1.0.0.tgz", - "integrity": "sha512-+q/t7Ekv1EDY2l6Gda6LLiX14rU9TV20Wa3ofeQmwPFZbOMo9DXrLbOjFaaclkXKWidIaopwAObQDqwWtGUjqg==", - "dev": true - }, - "autoprefixer": { - "version": "10.4.4", - "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.4.tgz", - "integrity": "sha512-Tm8JxsB286VweiZ5F0anmbyGiNI3v3wGv3mz9W+cxEDYB/6jbnj6GM9H9mK3wIL8ftgl+C07Lcwb8PG5PCCPzA==", - "dev": true, - "requires": { - "browserslist": "^4.20.2", - "caniuse-lite": "^1.0.30001317", - "fraction.js": "^4.2.0", - "normalize-range": "^0.1.2", - "picocolors": "^1.0.0", - "postcss-value-parser": "^4.2.0" - } - }, - "babel-eslint": { - "version": "10.1.0", - "resolved": "https://registry.npmjs.org/babel-eslint/-/babel-eslint-10.1.0.tgz", - "integrity": "sha512-ifWaTHQ0ce+448CYop8AdrQiBsGrnC+bMgfyKFdi6EsPLTAWG+QfyDeM6OH+FmWnKvEq5NnBMLvlBUPKQZoDSg==", - "dev": true, - "requires": { - "@babel/code-frame": "^7.0.0", - "@babel/parser": "^7.7.0", - "@babel/traverse": "^7.7.0", - "@babel/types": "^7.7.0", - "eslint-visitor-keys": "^1.0.0", - "resolve": "^1.12.0" - } - }, - "babel-loader": { - "version": "8.2.4", - "resolved": "https://registry.npmjs.org/babel-loader/-/babel-loader-8.2.4.tgz", - "integrity": "sha512-8dytA3gcvPPPv4Grjhnt8b5IIiTcq/zeXOPk4iTYI0SVXcsmuGg7JtBRDp8S9X+gJfhQ8ektjXZlDu1Bb33U8A==", - "dev": true, - "requires": { - "find-cache-dir": "^3.3.1", - "loader-utils": "^2.0.0", - "make-dir": "^3.1.0", - "schema-utils": "^2.6.5" - } - }, - "babel-plugin-dynamic-import-node": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/babel-plugin-dynamic-import-node/-/babel-plugin-dynamic-import-node-2.3.3.tgz", - "integrity": "sha512-jZVI+s9Zg3IqA/kdi0i6UDCybUI3aSBLnglhYbSSjKlV7yF1F/5LWv8MakQmvYpnbJDS6fcBL2KzHSxNCMtWSQ==", - "dev": true, - "requires": { - "object.assign": "^4.1.0" - } - }, - "babel-plugin-polyfill-corejs2": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs2/-/babel-plugin-polyfill-corejs2-0.3.1.tgz", - "integrity": "sha512-v7/T6EQcNfVLfcN2X8Lulb7DjprieyLWJK/zOWH5DUYcAgex9sP3h25Q+DLsX9TloXe3y1O8l2q2Jv9q8UVB9w==", - "dev": true, - "requires": { - "@babel/compat-data": "^7.13.11", - "@babel/helper-define-polyfill-provider": "^0.3.1", - "semver": "^6.1.1" - } - }, - "babel-plugin-polyfill-corejs3": { - "version": "0.5.2", - "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.5.2.tgz", - "integrity": "sha512-G3uJih0XWiID451fpeFaYGVuxHEjzKTHtc9uGFEjR6hHrvNzeS/PX+LLLcetJcytsB5m4j+K3o/EpXJNb/5IEQ==", - "dev": true, - "requires": { - "@babel/helper-define-polyfill-provider": "^0.3.1", - "core-js-compat": "^3.21.0" - } - }, - "babel-plugin-polyfill-regenerator": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-regenerator/-/babel-plugin-polyfill-regenerator-0.3.1.tgz", - "integrity": "sha512-Y2B06tvgHYt1x0yz17jGkGeeMr5FeKUu+ASJ+N6nB5lQ8Dapfg42i0OVrf8PNGJ3zKL4A23snMi1IRwrqqND7A==", - "dev": true, - "requires": { - "@babel/helper-define-polyfill-provider": "^0.3.1" - } - }, - "balanced-match": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", - "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", - "dev": true - }, - "base64-js": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", - "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", - "dev": true - }, - "batch": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/batch/-/batch-0.6.1.tgz", - "integrity": "sha1-3DQxT05nkxgJP8dgJyUl+UvyXBY=", - "dev": true - }, - "big.js": { - "version": "5.2.2", - "resolved": "https://registry.npmjs.org/big.js/-/big.js-5.2.2.tgz", - "integrity": "sha512-vyL2OymJxmarO8gxMr0mhChsO9QGwhynfuu4+MHTAW6czfq9humCB7rKpUjDd9YUiDPU4mzpyupFSvOClAwbmQ==", - "dev": true - }, - "binary-extensions": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", - "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==", - "dev": true - }, - "bl": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz", - "integrity": "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==", - "dev": true, - "requires": { - "buffer": "^5.5.0", - "inherits": "^2.0.4", - "readable-stream": "^3.4.0" - } - }, - "bluebird": { - "version": "3.7.2", - "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.7.2.tgz", - "integrity": "sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg==", - "dev": true - }, - "body-parser": { - "version": "1.19.2", - "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.19.2.tgz", - "integrity": "sha512-SAAwOxgoCKMGs9uUAUFHygfLAyaniaoun6I8mFY9pRAJL9+Kec34aU+oIjDhTycub1jozEfEwx1W1IuOYxVSFw==", - "dev": true, - "requires": { - "bytes": "3.1.2", - "content-type": "~1.0.4", - "debug": "2.6.9", - "depd": "~1.1.2", - "http-errors": "1.8.1", - "iconv-lite": "0.4.24", - "on-finished": "~2.3.0", - "qs": "6.9.7", - "raw-body": "2.4.3", - "type-is": "~1.6.18" - }, - "dependencies": { - "bytes": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", - "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", - "dev": true - }, - "debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "dev": true, - "requires": { - "ms": "2.0.0" - } - }, - "ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", - "dev": true - } - } - }, - "bonjour-service": { - "version": "1.0.11", - "resolved": "https://registry.npmjs.org/bonjour-service/-/bonjour-service-1.0.11.tgz", - "integrity": "sha512-drMprzr2rDTCtgEE3VgdA9uUFaUHF+jXduwYSThHJnKMYM+FhI9Z3ph+TX3xy0LtgYHae6CHYPJ/2UnK8nQHcA==", - "dev": true, - "requires": { - "array-flatten": "^2.1.2", - "dns-equal": "^1.0.0", - "fast-deep-equal": "^3.1.3", - "multicast-dns": "^7.2.4" - } - }, - "boolbase": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz", - "integrity": "sha1-aN/1++YMUes3cl6p4+0xDcwed24=", - "dev": true - }, - "brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "dev": true, - "requires": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "braces": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", - "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", - "dev": true, - "requires": { - "fill-range": "^7.0.1" - } - }, - "browserslist": { - "version": "4.20.2", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.20.2.tgz", - "integrity": "sha512-CQOBCqp/9pDvDbx3xfMi+86pr4KXIf2FDkTTdeuYw8OxS9t898LA1Khq57gtufFILXpfgsSx5woNgsBgvGjpsA==", - "dev": true, - "requires": { - "caniuse-lite": "^1.0.30001317", - "electron-to-chromium": "^1.4.84", - "escalade": "^3.1.1", - "node-releases": "^2.0.2", - "picocolors": "^1.0.0" - } - }, - "buffer": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", - "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", - "dev": true, - "requires": { - "base64-js": "^1.3.1", - "ieee754": "^1.1.13" - } - }, - "buffer-from": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", - "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", - "dev": true - }, - "bytes": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.0.0.tgz", - "integrity": "sha1-0ygVQE1olpn4Wk6k+odV3ROpYEg=", - "dev": true - }, - "call-bind": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz", - "integrity": "sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==", - "dev": true, - "requires": { - "function-bind": "^1.1.1", - "get-intrinsic": "^1.0.2" - } - }, - "callsites": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", - "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", - "dev": true - }, - "camel-case": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/camel-case/-/camel-case-4.1.2.tgz", - "integrity": "sha512-gxGWBrTT1JuMx6R+o5PTXMmUnhnVzLQ9SNutD4YqKtI6ap897t3tKECYla6gCWEkplXnlNybEkZg9GEGxKFCgw==", - "dev": true, - "requires": { - "pascal-case": "^3.1.2", - "tslib": "^2.0.3" - } - }, - "camelcase": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", - "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", - "dev": true - }, - "caniuse-api": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/caniuse-api/-/caniuse-api-3.0.0.tgz", - "integrity": "sha512-bsTwuIg/BZZK/vreVTYYbSWoe2F+71P7K5QGEX+pT250DZbfU1MQ5prOKpPR+LL6uWKK3KMwMCAS74QB3Um1uw==", - "dev": true, - "requires": { - "browserslist": "^4.0.0", - "caniuse-lite": "^1.0.0", - "lodash.memoize": "^4.1.2", - "lodash.uniq": "^4.5.0" - } - }, - "caniuse-lite": { - "version": "1.0.30001325", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001325.tgz", - "integrity": "sha512-sB1bZHjseSjDtijV1Hb7PB2Zd58Kyx+n/9EotvZ4Qcz2K3d0lWB8dB4nb8wN/TsOGFq3UuAm0zQZNQ4SoR7TrQ==", - "dev": true - }, - "case-sensitive-paths-webpack-plugin": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/case-sensitive-paths-webpack-plugin/-/case-sensitive-paths-webpack-plugin-2.4.0.tgz", - "integrity": "sha512-roIFONhcxog0JSSWbvVAh3OocukmSgpqOH6YpMkCvav/ySIV3JKg4Dc8vYtQjYi/UxpNE36r/9v+VqTQqgkYmw==", - "dev": true - }, - "chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", - "dev": true, - "requires": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - } - }, - "charcodes": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/charcodes/-/charcodes-0.2.0.tgz", - "integrity": "sha512-Y4kiDb+AM4Ecy58YkuZrrSRJBDQdQ2L+NyS1vHHFtNtUjgutcZfx3yp1dAONI/oPaPmyGfCLx5CxL+zauIMyKQ==", - "dev": true - }, - "chokidar": { - "version": "3.5.3", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz", - "integrity": "sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==", - "dev": true, - "requires": { - "anymatch": "~3.1.2", - "braces": "~3.0.2", - "fsevents": "~2.3.2", - "glob-parent": "~5.1.2", - "is-binary-path": "~2.1.0", - "is-glob": "~4.0.1", - "normalize-path": "~3.0.0", - "readdirp": "~3.6.0" - } - }, - "chrome-trace-event": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/chrome-trace-event/-/chrome-trace-event-1.0.3.tgz", - "integrity": "sha512-p3KULyQg4S7NIHixdwbGX+nFHkoBiA4YQmyWtjb8XngSKV124nJmRysgAeujbUVb15vh+RvFUfCPqU7rXk+hZg==", - "dev": true - }, - "clean-css": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/clean-css/-/clean-css-5.3.0.tgz", - "integrity": "sha512-YYuuxv4H/iNb1Z/5IbMRoxgrzjWGhOEFfd+groZ5dMCVkpENiMZmwspdrzBo9286JjM1gZJPAyL7ZIdzuvu2AQ==", - "dev": true, - "requires": { - "source-map": "~0.6.0" - } - }, - "cli-cursor": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-3.1.0.tgz", - "integrity": "sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==", - "dev": true, - "requires": { - "restore-cursor": "^3.1.0" - } - }, - "cli-highlight": { - "version": "2.1.11", - "resolved": "https://registry.npmjs.org/cli-highlight/-/cli-highlight-2.1.11.tgz", - "integrity": "sha512-9KDcoEVwyUXrjcJNvHD0NFc/hiwe/WPVYIleQh2O1N2Zro5gWJZ/K+3DGn8w8P/F6FxOgzyC5bxDyHIgCSPhGg==", - "dev": true, - "requires": { - "chalk": "^4.0.0", - "highlight.js": "^10.7.1", - "mz": "^2.4.0", - "parse5": "^5.1.1", - "parse5-htmlparser2-tree-adapter": "^6.0.0", - "yargs": "^16.0.0" - }, - "dependencies": { - "ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "requires": { - "color-convert": "^2.0.1" - } - }, - "chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - } - }, - "color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "requires": { - "color-name": "~1.1.4" - } - }, - "color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true - }, - "supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "requires": { - "has-flag": "^4.0.0" - } - } - } - }, - "cli-spinners": { - "version": "2.6.1", - "resolved": "https://registry.npmjs.org/cli-spinners/-/cli-spinners-2.6.1.tgz", - "integrity": "sha512-x/5fWmGMnbKQAaNwN+UZlV79qBLM9JFnJuJ03gIi5whrob0xV0ofNVHy9DhwGdsMJQc2OKv0oGmLzvaqvAVv+g==", - "dev": true - }, - "clipboardy": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/clipboardy/-/clipboardy-2.3.0.tgz", - "integrity": "sha512-mKhiIL2DrQIsuXMgBgnfEHOZOryC7kY7YO//TN6c63wlEm3NG5tz+YgY5rVi29KCmq/QQjKYvM7a19+MDOTHOQ==", - "dev": true, - "requires": { - "arch": "^2.1.1", - "execa": "^1.0.0", - "is-wsl": "^2.1.1" - } - }, - "cliui": { - "version": "7.0.4", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", - "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", - "dev": true, - "requires": { - "string-width": "^4.2.0", - "strip-ansi": "^6.0.0", - "wrap-ansi": "^7.0.0" - } - }, - "clone": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/clone/-/clone-1.0.4.tgz", - "integrity": "sha1-2jCcwmPfFZlMaIypAheco8fNfH4=", - "dev": true - }, - "clone-deep": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/clone-deep/-/clone-deep-4.0.1.tgz", - "integrity": "sha512-neHB9xuzh/wk0dIHweyAXv2aPGZIVk3pLMe+/RNzINf17fe0OG96QroktYAUm7SM1PBnzTabaLboqqxDyMU+SQ==", - "dev": true, - "requires": { - "is-plain-object": "^2.0.4", - "kind-of": "^6.0.2", - "shallow-clone": "^3.0.0" - } - }, - "color-convert": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", - "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", - "dev": true, - "requires": { - "color-name": "1.1.3" - } - }, - "color-name": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", - "dev": true - }, - "colord": { - "version": "2.9.2", - "resolved": "https://registry.npmjs.org/colord/-/colord-2.9.2.tgz", - "integrity": "sha512-Uqbg+J445nc1TKn4FoDPS6ZZqAvEDnwrH42yo8B40JSOgSLxMZ/gt3h4nmCtPLQeXhjJJkqBx7SCY35WnIixaQ==", - "dev": true - }, - "colorette": { - "version": "2.0.16", - "resolved": "https://registry.npmjs.org/colorette/-/colorette-2.0.16.tgz", - "integrity": "sha512-hUewv7oMjCp+wkBv5Rm0v87eJhq4woh5rSR+42YSQJKecCqgIqNkZ6lAlQms/BwHPJA5NKMRlpxPRv0n8HQW6g==", - "dev": true - }, - "commander": { - "version": "2.20.3", - "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", - "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", - "dev": true - }, - "commondir": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz", - "integrity": "sha1-3dgA2gxmEnOTzKWVDqloo6rxJTs=", - "dev": true - }, - "compressible": { - "version": "2.0.18", - "resolved": "https://registry.npmjs.org/compressible/-/compressible-2.0.18.tgz", - "integrity": "sha512-AF3r7P5dWxL8MxyITRMlORQNaOA2IkAFaTr4k7BUumjPtRpGDTZpl0Pb1XCO6JeDCBdp126Cgs9sMxqSjgYyRg==", - "dev": true, - "requires": { - "mime-db": ">= 1.43.0 < 2" - } - }, - "compression": { - "version": "1.7.4", - "resolved": "https://registry.npmjs.org/compression/-/compression-1.7.4.tgz", - "integrity": "sha512-jaSIDzP9pZVS4ZfQ+TzvtiWhdpFhE2RDHz8QJkpX9SIpLq88VueF5jJw6t+6CUQcAoA6t+x89MLrWAqpfDE8iQ==", - "dev": true, - "requires": { - "accepts": "~1.3.5", - "bytes": "3.0.0", - "compressible": "~2.0.16", - "debug": "2.6.9", - "on-headers": "~1.0.2", - "safe-buffer": "5.1.2", - "vary": "~1.1.2" - }, - "dependencies": { - "debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "dev": true, - "requires": { - "ms": "2.0.0" - } - }, - "ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", - "dev": true - } - } - }, - "concat-map": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", - "dev": true - }, - "connect-history-api-fallback": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/connect-history-api-fallback/-/connect-history-api-fallback-1.6.0.tgz", - "integrity": "sha512-e54B99q/OUoH64zYYRf3HBP5z24G38h5D3qXu23JGRoigpX5Ss4r9ZnDk3g0Z8uQC2x2lPaJ+UlWBc1ZWBWdLg==", - "dev": true - }, - "consolidate": { - "version": "0.15.1", - "resolved": "https://registry.npmjs.org/consolidate/-/consolidate-0.15.1.tgz", - "integrity": "sha512-DW46nrsMJgy9kqAbPt5rKaCr7uFtpo4mSUvLHIUbJEjm0vo+aY5QLwBUq3FK4tRnJr/X0Psc0C4jf/h+HtXSMw==", - "dev": true, - "requires": { - "bluebird": "^3.1.1" - } - }, - "content-disposition": { - "version": "0.5.4", - "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz", - "integrity": "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==", - "dev": true, - "requires": { - "safe-buffer": "5.2.1" - }, - "dependencies": { - "safe-buffer": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", - "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", - "dev": true - } - } - }, - "content-type": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz", - "integrity": "sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA==", - "dev": true - }, - "convert-source-map": { - "version": "1.8.0", - "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.8.0.tgz", - "integrity": "sha512-+OQdjP49zViI/6i7nIJpA8rAl4sV/JdPfU9nZs3VqOwGIgizICvuN2ru6fMd+4llL0tar18UYJXfZ/TWtmhUjA==", - "dev": true, - "requires": { - "safe-buffer": "~5.1.1" - } - }, - "cookie": { - "version": "0.4.2", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.2.tgz", - "integrity": "sha512-aSWTXFzaKWkvHO1Ny/s+ePFpvKsPnjc551iI41v3ny/ow6tBG5Vd+FuqGNhh1LxOmVzOlGUriIlOaokOvhaStA==", - "dev": true - }, - "cookie-signature": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", - "integrity": "sha1-4wOogrNCzD7oylE6eZmXNNqzriw=", - "dev": true - }, - "copy-webpack-plugin": { - "version": "9.1.0", - "resolved": "https://registry.npmjs.org/copy-webpack-plugin/-/copy-webpack-plugin-9.1.0.tgz", - "integrity": "sha512-rxnR7PaGigJzhqETHGmAcxKnLZSR5u1Y3/bcIv/1FnqXedcL/E2ewK7ZCNrArJKCiSv8yVXhTqetJh8inDvfsA==", - "dev": true, - "requires": { - "fast-glob": "^3.2.7", - "glob-parent": "^6.0.1", - "globby": "^11.0.3", - "normalize-path": "^3.0.0", - "schema-utils": "^3.1.1", - "serialize-javascript": "^6.0.0" - }, - "dependencies": { - "glob-parent": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", - "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", - "dev": true, - "requires": { - "is-glob": "^4.0.3" - } - }, - "schema-utils": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.1.1.tgz", - "integrity": "sha512-Y5PQxS4ITlC+EahLuXaY86TXfR7Dc5lw294alXOq86JAHCihAIZfqv8nNCWvaEJvaC51uN9hbLGeV0cFBdH+Fw==", - "dev": true, - "requires": { - "@types/json-schema": "^7.0.8", - "ajv": "^6.12.5", - "ajv-keywords": "^3.5.2" - } - } - } - }, - "core-js": { - "version": "3.21.1", - "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.21.1.tgz", - "integrity": "sha512-FRq5b/VMrWlrmCzwRrpDYNxyHP9BcAZC+xHJaqTgIE5091ZV1NTmyh0sGOg5XqpnHvR0svdy0sv1gWA1zmhxig==", - "dev": true - }, - "core-js-compat": { - "version": "3.21.1", - "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.21.1.tgz", - "integrity": "sha512-gbgX5AUvMb8gwxC7FLVWYT7Kkgu/y7+h/h1X43yJkNqhlK2fuYyQimqvKGNZFAY6CKii/GFKJ2cp/1/42TN36g==", - "dev": true, - "requires": { - "browserslist": "^4.19.1", - "semver": "7.0.0" - }, - "dependencies": { - "semver": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.0.0.tgz", - "integrity": "sha512-+GB6zVA9LWh6zovYQLALHwv5rb2PHGlJi3lfiqIHxR0uuwCgefcOJc59v9fv1w8GbStwxuuqqAjI9NMAOOgq1A==", - "dev": true - } - } - }, - "core-util-is": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz", - "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==", - "dev": true - }, - "cosmiconfig": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-7.0.1.tgz", - "integrity": "sha512-a1YWNUV2HwGimB7dU2s1wUMurNKjpx60HxBB6xUM8Re+2s1g1IIfJvFR0/iCF+XHdE0GMTKTuLR32UQff4TEyQ==", - "dev": true, - "requires": { - "@types/parse-json": "^4.0.0", - "import-fresh": "^3.2.1", - "parse-json": "^5.0.0", - "path-type": "^4.0.0", - "yaml": "^1.10.0" - } - }, - "cross-spawn": { - "version": "6.0.5", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz", - "integrity": "sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==", - "dev": true, - "requires": { - "nice-try": "^1.0.4", - "path-key": "^2.0.1", - "semver": "^5.5.0", - "shebang-command": "^1.2.0", - "which": "^1.2.9" - }, - "dependencies": { - "semver": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", - "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", - "dev": true - } - } - }, - "css-declaration-sorter": { - "version": "6.2.2", - "resolved": "https://registry.npmjs.org/css-declaration-sorter/-/css-declaration-sorter-6.2.2.tgz", - "integrity": "sha512-Ufadglr88ZLsrvS11gjeu/40Lw74D9Am/Jpr3LlYm5Q4ZP5KdlUhG+6u2EjyXeZcxmZ2h1ebCKngDjolpeLHpg==", - "dev": true - }, - "css-loader": { - "version": "6.7.1", - "resolved": "https://registry.npmjs.org/css-loader/-/css-loader-6.7.1.tgz", - "integrity": "sha512-yB5CNFa14MbPJcomwNh3wLThtkZgcNyI2bNMRt8iE5Z8Vwl7f8vQXFAzn2HDOJvtDq2NTZBUGMSUNNyrv3/+cw==", - "dev": true, - "requires": { - "icss-utils": "^5.1.0", - "postcss": "^8.4.7", - "postcss-modules-extract-imports": "^3.0.0", - "postcss-modules-local-by-default": "^4.0.0", - "postcss-modules-scope": "^3.0.0", - "postcss-modules-values": "^4.0.0", - "postcss-value-parser": "^4.2.0", - "semver": "^7.3.5" - }, - "dependencies": { - "semver": { - "version": "7.3.5", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz", - "integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==", - "dev": true, - "requires": { - "lru-cache": "^6.0.0" - } - } - } - }, - "css-minimizer-webpack-plugin": { - "version": "3.4.1", - "resolved": "https://registry.npmjs.org/css-minimizer-webpack-plugin/-/css-minimizer-webpack-plugin-3.4.1.tgz", - "integrity": "sha512-1u6D71zeIfgngN2XNRJefc/hY7Ybsxd74Jm4qngIXyUEk7fss3VUzuHxLAq/R8NAba4QU9OUSaMZlbpRc7bM4Q==", - "dev": true, - "requires": { - "cssnano": "^5.0.6", - "jest-worker": "^27.0.2", - "postcss": "^8.3.5", - "schema-utils": "^4.0.0", - "serialize-javascript": "^6.0.0", - "source-map": "^0.6.1" - }, - "dependencies": { - "ajv": { - "version": "8.11.0", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.11.0.tgz", - "integrity": "sha512-wGgprdCvMalC0BztXvitD2hC04YffAvtsUn93JbGXYLAtCUO4xd17mCCZQxUOItiBwZvJScWo8NIvQMQ71rdpg==", - "dev": true, - "requires": { - "fast-deep-equal": "^3.1.1", - "json-schema-traverse": "^1.0.0", - "require-from-string": "^2.0.2", - "uri-js": "^4.2.2" - } - }, - "ajv-keywords": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-5.1.0.tgz", - "integrity": "sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw==", - "dev": true, - "requires": { - "fast-deep-equal": "^3.1.3" - } - }, - "json-schema-traverse": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", - "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", - "dev": true - }, - "schema-utils": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.0.0.tgz", - "integrity": "sha512-1edyXKgh6XnJsJSQ8mKWXnN/BVaIbFMLpouRUrXgVq7WYne5kw3MW7UPhO44uRXQSIpTSXoJbmrR2X0w9kUTyg==", - "dev": true, - "requires": { - "@types/json-schema": "^7.0.9", - "ajv": "^8.8.0", - "ajv-formats": "^2.1.1", - "ajv-keywords": "^5.0.0" - } - } - } - }, - "css-select": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/css-select/-/css-select-4.3.0.tgz", - "integrity": "sha512-wPpOYtnsVontu2mODhA19JrqWxNsfdatRKd64kmpRbQgh1KtItko5sTnEpPdpSaJszTOhEMlF/RPz28qj4HqhQ==", - "dev": true, - "requires": { - "boolbase": "^1.0.0", - "css-what": "^6.0.1", - "domhandler": "^4.3.1", - "domutils": "^2.8.0", - "nth-check": "^2.0.1" - } - }, - "css-tree": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-1.1.3.tgz", - "integrity": "sha512-tRpdppF7TRazZrjJ6v3stzv93qxRcSsFmW6cX0Zm2NVKpxE1WV1HblnghVv9TreireHkqI/VDEsfolRF1p6y7Q==", - "dev": true, - "requires": { - "mdn-data": "2.0.14", - "source-map": "^0.6.1" - } - }, - "css-what": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/css-what/-/css-what-6.1.0.tgz", - "integrity": "sha512-HTUrgRJ7r4dsZKU6GjmpfRK1O76h97Z8MfS1G0FozR+oF2kG6Vfe8JE6zwrkbxigziPHinCJ+gCPjA9EaBDtRw==", - "dev": true - }, - "cssesc": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz", - "integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==", - "dev": true - }, - "cssnano": { - "version": "5.1.7", - "resolved": "https://registry.npmjs.org/cssnano/-/cssnano-5.1.7.tgz", - "integrity": "sha512-pVsUV6LcTXif7lvKKW9ZrmX+rGRzxkEdJuVJcp5ftUjWITgwam5LMZOgaTvUrWPkcORBey6he7JKb4XAJvrpKg==", - "dev": true, - "requires": { - "cssnano-preset-default": "^5.2.7", - "lilconfig": "^2.0.3", - "yaml": "^1.10.2" - } - }, - "cssnano-preset-default": { - "version": "5.2.7", - "resolved": "https://registry.npmjs.org/cssnano-preset-default/-/cssnano-preset-default-5.2.7.tgz", - "integrity": "sha512-JiKP38ymZQK+zVKevphPzNSGHSlTI+AOwlasoSRtSVMUU285O7/6uZyd5NbW92ZHp41m0sSHe6JoZosakj63uA==", - "dev": true, - "requires": { - "css-declaration-sorter": "^6.2.2", - "cssnano-utils": "^3.1.0", - "postcss-calc": "^8.2.3", - "postcss-colormin": "^5.3.0", - "postcss-convert-values": "^5.1.0", - "postcss-discard-comments": "^5.1.1", - "postcss-discard-duplicates": "^5.1.0", - "postcss-discard-empty": "^5.1.1", - "postcss-discard-overridden": "^5.1.0", - "postcss-merge-longhand": "^5.1.4", - "postcss-merge-rules": "^5.1.1", - "postcss-minify-font-values": "^5.1.0", - "postcss-minify-gradients": "^5.1.1", - "postcss-minify-params": "^5.1.2", - "postcss-minify-selectors": "^5.2.0", - "postcss-normalize-charset": "^5.1.0", - "postcss-normalize-display-values": "^5.1.0", - "postcss-normalize-positions": "^5.1.0", - "postcss-normalize-repeat-style": "^5.1.0", - "postcss-normalize-string": "^5.1.0", - "postcss-normalize-timing-functions": "^5.1.0", - "postcss-normalize-unicode": "^5.1.0", - "postcss-normalize-url": "^5.1.0", - "postcss-normalize-whitespace": "^5.1.1", - "postcss-ordered-values": "^5.1.1", - "postcss-reduce-initial": "^5.1.0", - "postcss-reduce-transforms": "^5.1.0", - "postcss-svgo": "^5.1.0", - "postcss-unique-selectors": "^5.1.1" - } - }, - "cssnano-utils": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/cssnano-utils/-/cssnano-utils-3.1.0.tgz", - "integrity": "sha512-JQNR19/YZhz4psLX/rQ9M83e3z2Wf/HdJbryzte4a3NSuafyp9w/I4U+hx5C2S9g41qlstH7DEWnZaaj83OuEA==", - "dev": true - }, - "csso": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/csso/-/csso-4.2.0.tgz", - "integrity": "sha512-wvlcdIbf6pwKEk7vHj8/Bkc0B4ylXZruLvOgs9doS5eOsOpuodOV2zJChSpkp+pRpYQLQMeF04nr3Z68Sta9jA==", - "dev": true, - "requires": { - "css-tree": "^1.1.2" - } - }, - "csstype": { - "version": "2.6.20", - "resolved": "https://registry.npmjs.org/csstype/-/csstype-2.6.20.tgz", - "integrity": "sha512-/WwNkdXfckNgw6S5R125rrW8ez139lBHWouiBvX8dfMFtcn6V81REDqnH7+CRpRipfYlyU1CmOnOxrmGcFOjeA==" - }, - "debug": { - "version": "4.3.4", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", - "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", - "dev": true, - "requires": { - "ms": "2.1.2" - } - }, - "deepmerge": { - "version": "1.5.2", - "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-1.5.2.tgz", - "integrity": "sha512-95k0GDqvBjZavkuvzx/YqVLv/6YYa17fz6ILMSf7neqQITCPbnfEnQvEgMPNjH4kgobe7+WIL0yJEHku+H3qtQ==", - "dev": true - }, - "default-gateway": { - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/default-gateway/-/default-gateway-6.0.3.tgz", - "integrity": "sha512-fwSOJsbbNzZ/CUFpqFBqYfYNLj1NbMPm8MMCIzHjC83iSJRBEGmDUxU+WP661BaBQImeC2yHwXtz+P/O9o+XEg==", - "dev": true, - "requires": { - "execa": "^5.0.0" - }, - "dependencies": { - "cross-spawn": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", - "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", - "dev": true, - "requires": { - "path-key": "^3.1.0", - "shebang-command": "^2.0.0", - "which": "^2.0.1" - } - }, - "execa": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz", - "integrity": "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==", - "dev": true, - "requires": { - "cross-spawn": "^7.0.3", - "get-stream": "^6.0.0", - "human-signals": "^2.1.0", - "is-stream": "^2.0.0", - "merge-stream": "^2.0.0", - "npm-run-path": "^4.0.1", - "onetime": "^5.1.2", - "signal-exit": "^3.0.3", - "strip-final-newline": "^2.0.0" - } - }, - "get-stream": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", - "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==", - "dev": true - }, - "is-stream": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", - "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", - "dev": true - }, - "npm-run-path": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz", - "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==", - "dev": true, - "requires": { - "path-key": "^3.0.0" - } - }, - "path-key": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", - "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", - "dev": true - }, - "shebang-command": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", - "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", - "dev": true, - "requires": { - "shebang-regex": "^3.0.0" - } - }, - "shebang-regex": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", - "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", - "dev": true - }, - "which": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", - "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", - "dev": true, - "requires": { - "isexe": "^2.0.0" - } - } - } - }, - "defaults": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/defaults/-/defaults-1.0.3.tgz", - "integrity": "sha1-xlYFHpgX2f8I7YgUd/P+QBnz730=", - "dev": true, - "requires": { - "clone": "^1.0.2" - } - }, - "define-lazy-prop": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/define-lazy-prop/-/define-lazy-prop-2.0.0.tgz", - "integrity": "sha512-Ds09qNh8yw3khSjiJjiUInaGX9xlqZDY7JVryGxdxV7NPeuqQfplOpQ66yJFZut3jLa5zOwkXw1g9EI2uKh4Og==", - "dev": true - }, - "define-properties": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.3.tgz", - "integrity": "sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ==", - "dev": true, - "requires": { - "object-keys": "^1.0.12" - } - }, - "depd": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", - "integrity": "sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak=", - "dev": true - }, - "destroy": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz", - "integrity": "sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA=", - "dev": true - }, - "detect-node": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/detect-node/-/detect-node-2.1.0.tgz", - "integrity": "sha512-T0NIuQpnTvFDATNuHN5roPwSBG83rFsuO+MXXH9/3N1eFbn4wcPjttvjMLEPWJ0RGUYgQE7cGgS3tNxbqCGM7g==", - "dev": true - }, - "dir-glob": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", - "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==", - "dev": true, - "requires": { - "path-type": "^4.0.0" - } - }, - "dns-equal": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/dns-equal/-/dns-equal-1.0.0.tgz", - "integrity": "sha1-s55/HabrCnW6nBcySzR1PEfgZU0=", - "dev": true - }, - "dns-packet": { - "version": "5.3.1", - "resolved": "https://registry.npmjs.org/dns-packet/-/dns-packet-5.3.1.tgz", - "integrity": "sha512-spBwIj0TK0Ey3666GwIdWVfUpLyubpU53BTCu8iPn4r4oXd9O14Hjg3EHw3ts2oed77/SeckunUYCyRlSngqHw==", - "dev": true, - "requires": { - "@leichtgewicht/ip-codec": "^2.0.1" - } - }, - "dom-converter": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/dom-converter/-/dom-converter-0.2.0.tgz", - "integrity": "sha512-gd3ypIPfOMr9h5jIKq8E3sHOTCjeirnl0WK5ZdS1AW0Odt0b1PaWaHdJ4Qk4klv+YB9aJBS7mESXjFoDQPu6DA==", - "dev": true, - "requires": { - "utila": "~0.4" - } - }, - "dom-serializer": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-1.3.2.tgz", - "integrity": "sha512-5c54Bk5Dw4qAxNOI1pFEizPSjVsx5+bpJKmL2kPn8JhBUq2q09tTCa3mjijun2NfK78NMouDYNMBkOrPZiS+ig==", - "dev": true, - "requires": { - "domelementtype": "^2.0.1", - "domhandler": "^4.2.0", - "entities": "^2.0.0" - } - }, - "domelementtype": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.2.0.tgz", - "integrity": "sha512-DtBMo82pv1dFtUmHyr48beiuq792Sxohr+8Hm9zoxklYPfa6n0Z3Byjj2IV7bmr2IyqClnqEQhfgHJJ5QF0R5A==", - "dev": true - }, - "domhandler": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-4.3.1.tgz", - "integrity": "sha512-GrwoxYN+uWlzO8uhUXRl0P+kHE4GtVPfYzVLcUxPL7KNdHKj66vvlhiweIHqYYXWlw+T8iLMp42Lm67ghw4WMQ==", - "dev": true, - "requires": { - "domelementtype": "^2.2.0" - } - }, - "domutils": { - "version": "2.8.0", - "resolved": "https://registry.npmjs.org/domutils/-/domutils-2.8.0.tgz", - "integrity": "sha512-w96Cjofp72M5IIhpjgobBimYEfoPjx1Vx0BSX9P30WBdZW2WIKU0T1Bd0kz2eNZ9ikjKgHbEyKx8BB6H1L3h3A==", - "dev": true, - "requires": { - "dom-serializer": "^1.0.1", - "domelementtype": "^2.2.0", - "domhandler": "^4.2.0" - } - }, - "dot-case": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/dot-case/-/dot-case-3.0.4.tgz", - "integrity": "sha512-Kv5nKlh6yRrdrGvxeJ2e5y2eRUpkUosIW4A2AS38zwSz27zu7ufDwQPi5Jhs3XAlGNetl3bmnGhQsMtkKJnj3w==", - "dev": true, - "requires": { - "no-case": "^3.0.4", - "tslib": "^2.0.3" - } - }, - "dotenv": { - "version": "10.0.0", - "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-10.0.0.tgz", - "integrity": "sha512-rlBi9d8jpv9Sf1klPjNfFAuWDjKLwTIJJ/VxtoTwIR6hnZxcEOQCZg2oIL3MWBYw5GpUDKOEnND7LXTbIpQ03Q==", - "dev": true - }, - "dotenv-expand": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/dotenv-expand/-/dotenv-expand-5.1.0.tgz", - "integrity": "sha512-YXQl1DSa4/PQyRfgrv6aoNjhasp/p4qs9FjJ4q4cQk+8m4r6k4ZSiEyytKG8f8W9gi8WsQtIObNmKd+tMzNTmA==", - "dev": true - }, - "duplexer": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/duplexer/-/duplexer-0.1.2.tgz", - "integrity": "sha512-jtD6YG370ZCIi/9GTaJKQxWTZD045+4R4hTk/x1UyoqadyJ9x9CgSi1RlVDQF8U2sxLLSnFkCaMihqljHIWgMg==", - "dev": true - }, - "easy-stack": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/easy-stack/-/easy-stack-1.0.1.tgz", - "integrity": "sha512-wK2sCs4feiiJeFXn3zvY0p41mdU5VUgbgs1rNsc/y5ngFUijdWd+iIN8eoyuZHKB8xN6BL4PdWmzqFmxNg6V2w==", - "dev": true - }, - "ee-first": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", - "integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=", - "dev": true - }, - "electron-to-chromium": { - "version": "1.4.104", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.104.tgz", - "integrity": "sha512-2kjoAyiG7uMyGRM9mx25s3HAzmQG2ayuYXxsFmYugHSDcwxREgLtscZvbL1JcW9S/OemeQ3f/SG6JhDwpnCclQ==", - "dev": true - }, - "emoji-regex": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", - "dev": true - }, - "emojis-list": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/emojis-list/-/emojis-list-3.0.0.tgz", - "integrity": "sha512-/kyM18EfinwXZbno9FyUGeFh87KC8HRQBQGildHZbEuRyWFOmv1U10o9BBp8XVZDVNNuQKyIGIu5ZYAAXJ0V2Q==", - "dev": true - }, - "encodeurl": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", - "integrity": "sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k=", - "dev": true - }, - "end-of-stream": { - "version": "1.4.4", - "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz", - "integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==", - "dev": true, - "requires": { - "once": "^1.4.0" - } - }, - "enhanced-resolve": { - "version": "5.9.2", - "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.9.2.tgz", - "integrity": "sha512-GIm3fQfwLJ8YZx2smuHpBKkXC1yOk+OBEmKckVyL0i/ea8mqDEykK3ld5dgH1QYPNyT/lIllxV2LULnxCHaHkA==", - "dev": true, - "requires": { - "graceful-fs": "^4.2.4", - "tapable": "^2.2.0" - } - }, - "entities": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/entities/-/entities-2.2.0.tgz", - "integrity": "sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A==", - "dev": true - }, - "error-ex": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", - "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", - "dev": true, - "requires": { - "is-arrayish": "^0.2.1" - } - }, - "error-stack-parser": { - "version": "2.0.7", - "resolved": "https://registry.npmjs.org/error-stack-parser/-/error-stack-parser-2.0.7.tgz", - "integrity": "sha512-chLOW0ZGRf4s8raLrDxa5sdkvPec5YdvwbFnqJme4rk0rFajP8mPtrDL1+I+CwrQDCjswDA5sREX7jYQDQs9vA==", - "dev": true, - "requires": { - "stackframe": "^1.1.1" - } - }, - "es-module-lexer": { - "version": "0.9.3", - "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-0.9.3.tgz", - "integrity": "sha512-1HQ2M2sPtxwnvOvT1ZClHyQDiggdNjURWpY2we6aMKCQiUVxTmVs2UYPLIrD84sS+kMdUwfBSylbJPwNnBrnHQ==", - "dev": true - }, - "escalade": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", - "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==", - "dev": true - }, - "escape-html": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", - "integrity": "sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg=", - "dev": true - }, - "escape-string-regexp": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", - "dev": true - }, - "eslint-scope": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", - "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==", - "dev": true, - "requires": { - "esrecurse": "^4.3.0", - "estraverse": "^4.1.1" - } - }, - "eslint-visitor-keys": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz", - "integrity": "sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ==", - "dev": true - }, - "esrecurse": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", - "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", - "dev": true, - "requires": { - "estraverse": "^5.2.0" - }, - "dependencies": { - "estraverse": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", - "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", - "dev": true - } - } - }, - "estraverse": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", - "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", - "dev": true - }, - "estree-walker": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz", - "integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==" - }, - "esutils": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", - "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", - "dev": true - }, - "etag": { - "version": "1.8.1", - "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", - "integrity": "sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc=", - "dev": true - }, - "event-pubsub": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/event-pubsub/-/event-pubsub-4.3.0.tgz", - "integrity": "sha512-z7IyloorXvKbFx9Bpie2+vMJKKx1fH1EN5yiTfp8CiLOTptSYy1g8H4yDpGlEdshL1PBiFtBHepF2cNsqeEeFQ==", - "dev": true - }, - "eventemitter3": { - "version": "4.0.7", - "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.7.tgz", - "integrity": "sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==", - "dev": true - }, - "events": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz", - "integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==", - "dev": true - }, - "execa": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/execa/-/execa-1.0.0.tgz", - "integrity": "sha512-adbxcyWV46qiHyvSp50TKt05tB4tK3HcmF7/nxfAdhnox83seTDbwnaqKO4sXRy7roHAIFqJP/Rw/AuEbX61LA==", - "dev": true, - "requires": { - "cross-spawn": "^6.0.0", - "get-stream": "^4.0.0", - "is-stream": "^1.1.0", - "npm-run-path": "^2.0.0", - "p-finally": "^1.0.0", - "signal-exit": "^3.0.0", - "strip-eof": "^1.0.0" - } - }, - "express": { - "version": "4.17.3", - "resolved": "https://registry.npmjs.org/express/-/express-4.17.3.tgz", - "integrity": "sha512-yuSQpz5I+Ch7gFrPCk4/c+dIBKlQUxtgwqzph132bsT6qhuzss6I8cLJQz7B3rFblzd6wtcI0ZbGltH/C4LjUg==", - "dev": true, - "requires": { - "accepts": "~1.3.8", - "array-flatten": "1.1.1", - "body-parser": "1.19.2", - "content-disposition": "0.5.4", - "content-type": "~1.0.4", - "cookie": "0.4.2", - "cookie-signature": "1.0.6", - "debug": "2.6.9", - "depd": "~1.1.2", - "encodeurl": "~1.0.2", - "escape-html": "~1.0.3", - "etag": "~1.8.1", - "finalhandler": "~1.1.2", - "fresh": "0.5.2", - "merge-descriptors": "1.0.1", - "methods": "~1.1.2", - "on-finished": "~2.3.0", - "parseurl": "~1.3.3", - "path-to-regexp": "0.1.7", - "proxy-addr": "~2.0.7", - "qs": "6.9.7", - "range-parser": "~1.2.1", - "safe-buffer": "5.2.1", - "send": "0.17.2", - "serve-static": "1.14.2", - "setprototypeof": "1.2.0", - "statuses": "~1.5.0", - "type-is": "~1.6.18", - "utils-merge": "1.0.1", - "vary": "~1.1.2" - }, - "dependencies": { - "array-flatten": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", - "integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI=", - "dev": true - }, - "debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "dev": true, - "requires": { - "ms": "2.0.0" - } - }, - "ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", - "dev": true - }, - "safe-buffer": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", - "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", - "dev": true - } - } - }, - "fast-deep-equal": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", - "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", - "dev": true - }, - "fast-glob": { - "version": "3.2.11", - "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.11.tgz", - "integrity": "sha512-xrO3+1bxSo3ZVHAnqzyuewYT6aMFHRAd4Kcs92MAonjwQZLsK9d0SF1IyQ3k5PoirxTW0Oe/RqFgMQ6TcNE5Ew==", - "dev": true, - "requires": { - "@nodelib/fs.stat": "^2.0.2", - "@nodelib/fs.walk": "^1.2.3", - "glob-parent": "^5.1.2", - "merge2": "^1.3.0", - "micromatch": "^4.0.4" - } - }, - "fast-json-stable-stringify": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", - "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", - "dev": true - }, - "fastq": { - "version": "1.13.0", - "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.13.0.tgz", - "integrity": "sha512-YpkpUnK8od0o1hmeSc7UUs/eB/vIPWJYjKck2QKIzAf71Vm1AAQ3EbuZB3g2JIy+pg+ERD0vqI79KyZiB2e2Nw==", - "dev": true, - "requires": { - "reusify": "^1.0.4" - } - }, - "faye-websocket": { - "version": "0.11.4", - "resolved": "https://registry.npmjs.org/faye-websocket/-/faye-websocket-0.11.4.tgz", - "integrity": "sha512-CzbClwlXAuiRQAlUyfqPgvPoNKTckTPGfwZV4ZdAhVcP2lh9KUxJg2b5GkE7XbjKQ3YJnQ9z6D9ntLAlB+tP8g==", - "dev": true, - "requires": { - "websocket-driver": ">=0.5.1" - } - }, - "figures": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/figures/-/figures-2.0.0.tgz", - "integrity": "sha1-OrGi0qYsi/tDGgyUy3l6L84nyWI=", - "dev": true, - "requires": { - "escape-string-regexp": "^1.0.5" - } - }, - "fill-range": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", - "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", - "dev": true, - "requires": { - "to-regex-range": "^5.0.1" - } - }, - "finalhandler": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.2.tgz", - "integrity": "sha512-aAWcW57uxVNrQZqFXjITpW3sIUQmHGG3qSb9mUah9MgMC4NeWhNOlNjXEYq3HjRAvL6arUviZGGJsBg6z0zsWA==", - "dev": true, - "requires": { - "debug": "2.6.9", - "encodeurl": "~1.0.2", - "escape-html": "~1.0.3", - "on-finished": "~2.3.0", - "parseurl": "~1.3.3", - "statuses": "~1.5.0", - "unpipe": "~1.0.0" - }, - "dependencies": { - "debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "dev": true, - "requires": { - "ms": "2.0.0" - } - }, - "ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", - "dev": true - } - } - }, - "find-cache-dir": { - "version": "3.3.2", - "resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-3.3.2.tgz", - "integrity": "sha512-wXZV5emFEjrridIgED11OoUKLxiYjAcqot/NJdAkOhlJ+vGzwhOAfcG5OX1jP+S0PcjEn8bdMJv+g2jwQ3Onig==", - "dev": true, - "requires": { - "commondir": "^1.0.1", - "make-dir": "^3.0.2", - "pkg-dir": "^4.1.0" - } - }, - "find-up": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", - "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", - "dev": true, - "requires": { - "locate-path": "^5.0.0", - "path-exists": "^4.0.0" - } - }, - "follow-redirects": { - "version": "1.14.9", - "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.14.9.tgz", - "integrity": "sha512-MQDfihBQYMcyy5dhRDJUHcw7lb2Pv/TuE6xP1vyraLukNDHKbDxDNaOE3NbCAdKQApno+GPRyo1YAp89yCjK4w==", - "dev": true - }, - "forwarded": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", - "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==", - "dev": true - }, - "fraction.js": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/fraction.js/-/fraction.js-4.2.0.tgz", - "integrity": "sha512-MhLuK+2gUcnZe8ZHlaaINnQLl0xRIGRfcGk2yl8xoQAfHrSsL3rYu6FCmBdkdbhc9EPlwyGHewaRsvwRMJtAlA==", - "dev": true - }, - "fresh": { - "version": "0.5.2", - "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", - "integrity": "sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac=", - "dev": true - }, - "fs-extra": { - "version": "9.1.0", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.1.0.tgz", - "integrity": "sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==", - "dev": true, - "requires": { - "at-least-node": "^1.0.0", - "graceful-fs": "^4.2.0", - "jsonfile": "^6.0.1", - "universalify": "^2.0.0" - } - }, - "fs-monkey": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/fs-monkey/-/fs-monkey-1.0.3.tgz", - "integrity": "sha512-cybjIfiiE+pTWicSCLFHSrXZ6EilF30oh91FDP9S2B051prEa7QWfrVTQm10/dDpswBDXZugPa1Ogu8Yh+HV0Q==", - "dev": true - }, - "fs.realpath": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", - "dev": true - }, - "fsevents": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", - "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", - "dev": true, - "optional": true - }, - "function-bind": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", - "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", - "dev": true - }, - "gensync": { - "version": "1.0.0-beta.2", - "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", - "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", - "dev": true - }, - "get-caller-file": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", - "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", - "dev": true - }, - "get-intrinsic": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.1.1.tgz", - "integrity": "sha512-kWZrnVM42QCiEA2Ig1bG8zjoIMOgxWwYCEeNdwY6Tv/cOSeGpcoX4pXHfKUxNKVoArnrEr2e9srnAxxGIraS9Q==", - "dev": true, - "requires": { - "function-bind": "^1.1.1", - "has": "^1.0.3", - "has-symbols": "^1.0.1" - } - }, - "get-stream": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-4.1.0.tgz", - "integrity": "sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w==", - "dev": true, - "requires": { - "pump": "^3.0.0" - } - }, - "glob": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.0.tgz", - "integrity": "sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q==", - "dev": true, - "requires": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.0.4", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - } - }, - "glob-parent": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", - "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", - "dev": true, - "requires": { - "is-glob": "^4.0.1" - } - }, - "glob-to-regexp": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/glob-to-regexp/-/glob-to-regexp-0.4.1.tgz", - "integrity": "sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw==", - "dev": true - }, - "globals": { - "version": "11.12.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", - "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", - "dev": true - }, - "globby": { - "version": "11.1.0", - "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz", - "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==", - "dev": true, - "requires": { - "array-union": "^2.1.0", - "dir-glob": "^3.0.1", - "fast-glob": "^3.2.9", - "ignore": "^5.2.0", - "merge2": "^1.4.1", - "slash": "^3.0.0" - } - }, - "graceful-fs": { - "version": "4.2.10", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.10.tgz", - "integrity": "sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA==", - "dev": true - }, - "gzip-size": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/gzip-size/-/gzip-size-6.0.0.tgz", - "integrity": "sha512-ax7ZYomf6jqPTQ4+XCpUGyXKHk5WweS+e05MBO4/y3WJ5RkmPXNKvX+bx1behVILVwr6JSQvZAku021CHPXG3Q==", - "dev": true, - "requires": { - "duplexer": "^0.1.2" - } - }, - "handle-thing": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/handle-thing/-/handle-thing-2.0.1.tgz", - "integrity": "sha512-9Qn4yBxelxoh2Ow62nP+Ka/kMnOXRi8BXnRaUwezLNhqelnN49xKz4F/dPP8OYLxLxq6JDtZb2i9XznUQbNPTg==", - "dev": true - }, - "has": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", - "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", - "dev": true, - "requires": { - "function-bind": "^1.1.1" - } - }, - "has-flag": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", - "dev": true - }, - "has-symbols": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", - "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==", - "dev": true - }, - "hash-sum": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/hash-sum/-/hash-sum-2.0.0.tgz", - "integrity": "sha512-WdZTbAByD+pHfl/g9QSsBIIwy8IT+EsPiKDs0KNX+zSHhdDLFKdZu0BQHljvO+0QI/BasbMSUa8wYNCZTvhslg==", - "dev": true - }, - "he": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", - "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==", - "dev": true - }, - "highlight.js": { - "version": "10.7.3", - "resolved": "https://registry.npmjs.org/highlight.js/-/highlight.js-10.7.3.tgz", - "integrity": "sha512-tzcUFauisWKNHaRkN4Wjl/ZA07gENAjFl3J/c480dprkGTg5EQstgaNFqBfUqCq54kZRIEcreTsAgF/m2quD7A==", - "dev": true - }, - "hosted-git-info": { - "version": "2.8.9", - "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.9.tgz", - "integrity": "sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==", - "dev": true - }, - "hpack.js": { - "version": "2.1.6", - "resolved": "https://registry.npmjs.org/hpack.js/-/hpack.js-2.1.6.tgz", - "integrity": "sha1-h3dMCUnlE/QuhFdbPEVoH63ioLI=", - "dev": true, - "requires": { - "inherits": "^2.0.1", - "obuf": "^1.0.0", - "readable-stream": "^2.0.1", - "wbuf": "^1.1.0" - }, - "dependencies": { - "readable-stream": { - "version": "2.3.7", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", - "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", - "dev": true, - "requires": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" - } - }, - "string_decoder": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", - "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", - "dev": true, - "requires": { - "safe-buffer": "~5.1.0" - } - } - } - }, - "html-entities": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/html-entities/-/html-entities-2.3.3.tgz", - "integrity": "sha512-DV5Ln36z34NNTDgnz0EWGBLZENelNAtkiFA4kyNOG2tDI6Mz1uSWiq1wAKdyjnJwyDiDO7Fa2SO1CTxPXL8VxA==", - "dev": true - }, - "html-minifier-terser": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/html-minifier-terser/-/html-minifier-terser-6.1.0.tgz", - "integrity": "sha512-YXxSlJBZTP7RS3tWnQw74ooKa6L9b9i9QYXY21eUEvhZ3u9XLfv6OnFsQq6RxkhHygsaUMvYsZRV5rU/OVNZxw==", - "dev": true, - "requires": { - "camel-case": "^4.1.2", - "clean-css": "^5.2.2", - "commander": "^8.3.0", - "he": "^1.2.0", - "param-case": "^3.0.4", - "relateurl": "^0.2.7", - "terser": "^5.10.0" - }, - "dependencies": { - "commander": { - "version": "8.3.0", - "resolved": "https://registry.npmjs.org/commander/-/commander-8.3.0.tgz", - "integrity": "sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww==", - "dev": true - } - } - }, - "html-tags": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/html-tags/-/html-tags-3.2.0.tgz", - "integrity": "sha512-vy7ClnArOZwCnqZgvv+ddgHgJiAFXe3Ge9ML5/mBctVJoUoYPCdxVucOywjDARn6CVoh3dRSFdPHy2sX80L0Wg==", - "dev": true - }, - "html-webpack-plugin": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/html-webpack-plugin/-/html-webpack-plugin-5.5.0.tgz", - "integrity": "sha512-sy88PC2cRTVxvETRgUHFrL4No3UxvcH8G1NepGhqaTT+GXN2kTamqasot0inS5hXeg1cMbFDt27zzo9p35lZVw==", - "dev": true, - "requires": { - "@types/html-minifier-terser": "^6.0.0", - "html-minifier-terser": "^6.0.2", - "lodash": "^4.17.21", - "pretty-error": "^4.0.0", - "tapable": "^2.0.0" - } - }, - "htmlparser2": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-6.1.0.tgz", - "integrity": "sha512-gyyPk6rgonLFEDGoeRgQNaEUvdJ4ktTmmUh/h2t7s+M8oPpIPxgNACWa+6ESR57kXstwqPiCut0V8NRpcwgU7A==", - "dev": true, - "requires": { - "domelementtype": "^2.0.1", - "domhandler": "^4.0.0", - "domutils": "^2.5.2", - "entities": "^2.0.0" - } - }, - "http-deceiver": { - "version": "1.2.7", - "resolved": "https://registry.npmjs.org/http-deceiver/-/http-deceiver-1.2.7.tgz", - "integrity": "sha1-+nFolEq5pRnTN8sL7HKE3D5yPYc=", - "dev": true - }, - "http-errors": { - "version": "1.8.1", - "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.8.1.tgz", - "integrity": "sha512-Kpk9Sm7NmI+RHhnj6OIWDI1d6fIoFAtFt9RLaTMRlg/8w49juAStsrBgp0Dp4OdxdVbRIeKhtCUvoi/RuAhO4g==", - "dev": true, - "requires": { - "depd": "~1.1.2", - "inherits": "2.0.4", - "setprototypeof": "1.2.0", - "statuses": ">= 1.5.0 < 2", - "toidentifier": "1.0.1" - } - }, - "http-parser-js": { - "version": "0.5.6", - "resolved": "https://registry.npmjs.org/http-parser-js/-/http-parser-js-0.5.6.tgz", - "integrity": "sha512-vDlkRPDJn93swjcjqMSaGSPABbIarsr1TLAui/gLDXzV5VsJNdXNzMYDyNBLQkjWQCJ1uizu8T2oDMhmGt0PRA==", - "dev": true - }, - "http-proxy": { - "version": "1.18.1", - "resolved": "https://registry.npmjs.org/http-proxy/-/http-proxy-1.18.1.tgz", - "integrity": "sha512-7mz/721AbnJwIVbnaSv1Cz3Am0ZLT/UBwkC92VlxhXv/k/BBQfM2fXElQNC27BVGr0uwUpplYPQM9LnaBMR5NQ==", - "dev": true, - "requires": { - "eventemitter3": "^4.0.0", - "follow-redirects": "^1.0.0", - "requires-port": "^1.0.0" - } - }, - "http-proxy-middleware": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/http-proxy-middleware/-/http-proxy-middleware-2.0.4.tgz", - "integrity": "sha512-m/4FxX17SUvz4lJ5WPXOHDUuCwIqXLfLHs1s0uZ3oYjhoXlx9csYxaOa0ElDEJ+h8Q4iJ1s+lTMbiCa4EXIJqg==", - "dev": true, - "requires": { - "@types/http-proxy": "^1.17.8", - "http-proxy": "^1.18.1", - "is-glob": "^4.0.1", - "is-plain-obj": "^3.0.0", - "micromatch": "^4.0.2" - } - }, - "human-signals": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz", - "integrity": "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==", - "dev": true - }, - "iconv-lite": { - "version": "0.4.24", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", - "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", - "dev": true, - "requires": { - "safer-buffer": ">= 2.1.2 < 3" - } - }, - "icss-utils": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/icss-utils/-/icss-utils-5.1.0.tgz", - "integrity": "sha512-soFhflCVWLfRNOPU3iv5Z9VUdT44xFRbzjLsEzSr5AQmgqPMTHdU3PMT1Cf1ssx8fLNJDA1juftYl+PUcv3MqA==", - "dev": true - }, - "ieee754": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", - "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", - "dev": true - }, - "ignore": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.0.tgz", - "integrity": "sha512-CmxgYGiEPCLhfLnpPp1MoRmifwEIOgjcHXxOBjv7mY96c+eWScsOP9c112ZyLdWHi0FxHjI+4uVhKYp/gcdRmQ==", - "dev": true - }, - "import-fresh": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", - "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==", - "dev": true, - "requires": { - "parent-module": "^1.0.0", - "resolve-from": "^4.0.0" - } - }, - "inflight": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", - "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", - "dev": true, - "requires": { - "once": "^1.3.0", - "wrappy": "1" - } - }, - "inherits": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", - "dev": true - }, - "ipaddr.js": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-2.0.1.tgz", - "integrity": "sha512-1qTgH9NG+IIJ4yfKs2e6Pp1bZg8wbDbKHT21HrLIeYBTRLgMYKnMTPAuI3Lcs61nfx5h1xlXnbJtH1kX5/d/ng==", - "dev": true - }, - "is-arrayish": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", - "integrity": "sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0=", - "dev": true - }, - "is-binary-path": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", - "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", - "dev": true, - "requires": { - "binary-extensions": "^2.0.0" - } - }, - "is-core-module": { - "version": "2.8.1", - "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.8.1.tgz", - "integrity": "sha512-SdNCUs284hr40hFTFP6l0IfZ/RSrMXF3qgoRHd3/79unUTvrFO/JoXwkGm+5J/Oe3E/b5GsnG330uUNgRpu1PA==", - "dev": true, - "requires": { - "has": "^1.0.3" - } - }, - "is-docker": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-2.2.1.tgz", - "integrity": "sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ==", - "dev": true - }, - "is-extglob": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", - "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=", - "dev": true - }, - "is-file-esm": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-file-esm/-/is-file-esm-1.0.0.tgz", - "integrity": "sha512-rZlaNKb4Mr8WlRu2A9XdeoKgnO5aA53XdPHgCKVyCrQ/rWi89RET1+bq37Ru46obaQXeiX4vmFIm1vks41hoSA==", - "dev": true, - "requires": { - "read-pkg-up": "^7.0.1" - } - }, - "is-fullwidth-code-point": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", - "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", - "dev": true - }, - "is-glob": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", - "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", - "dev": true, - "requires": { - "is-extglob": "^2.1.1" - } - }, - "is-interactive": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-interactive/-/is-interactive-1.0.0.tgz", - "integrity": "sha512-2HvIEKRoqS62guEC+qBjpvRubdX910WCMuJTZ+I9yvqKU2/12eSL549HMwtabb4oupdj2sMP50k+XJfB/8JE6w==", - "dev": true - }, - "is-number": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", - "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", - "dev": true - }, - "is-plain-obj": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-3.0.0.tgz", - "integrity": "sha512-gwsOE28k+23GP1B6vFl1oVh/WOzmawBrKwo5Ev6wMKzPkaXaCDIQKzLnvsA42DRlbVTWorkgTKIviAKCWkfUwA==", - "dev": true - }, - "is-plain-object": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz", - "integrity": "sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==", - "dev": true, - "requires": { - "isobject": "^3.0.1" - } - }, - "is-stream": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz", - "integrity": "sha1-EtSj3U5o4Lec6428hBc66A2RykQ=", - "dev": true - }, - "is-unicode-supported": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz", - "integrity": "sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==", - "dev": true - }, - "is-wsl": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-2.2.0.tgz", - "integrity": "sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==", - "dev": true, - "requires": { - "is-docker": "^2.0.0" - } - }, - "isarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", - "dev": true - }, - "isexe": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=", - "dev": true - }, - "isobject": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", - "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=", - "dev": true - }, - "javascript-stringify": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/javascript-stringify/-/javascript-stringify-2.1.0.tgz", - "integrity": "sha512-JVAfqNPTvNq3sB/VHQJAFxN/sPgKnsKrCwyRt15zwNCdrMMJDdcEOdubuy+DuJYYdm0ox1J4uzEuYKkN+9yhVg==", - "dev": true - }, - "jest-worker": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-27.5.1.tgz", - "integrity": "sha512-7vuh85V5cdDofPyxn58nrPjBktZo0u9x1g8WtjQol+jZDaE+fhN+cIvTj11GndBnMnyfrUOG1sZQxCdjKh+DKg==", - "dev": true, - "requires": { - "@types/node": "*", - "merge-stream": "^2.0.0", - "supports-color": "^8.0.0" - }, - "dependencies": { - "has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true - }, - "supports-color": { - "version": "8.1.1", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", - "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", - "dev": true, - "requires": { - "has-flag": "^4.0.0" - } - } - } - }, - "joi": { - "version": "17.6.0", - "resolved": "https://registry.npmjs.org/joi/-/joi-17.6.0.tgz", - "integrity": "sha512-OX5dG6DTbcr/kbMFj0KGYxuew69HPcAE3K/sZpEV2nP6e/j/C0HV+HNiBPCASxdx5T7DMoa0s8UeHWMnb6n2zw==", - "dev": true, - "requires": { - "@hapi/hoek": "^9.0.0", - "@hapi/topo": "^5.0.0", - "@sideway/address": "^4.1.3", - "@sideway/formula": "^3.0.0", - "@sideway/pinpoint": "^2.0.0" - } - }, - "js-message": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/js-message/-/js-message-1.0.7.tgz", - "integrity": "sha512-efJLHhLjIyKRewNS9EGZ4UpI8NguuL6fKkhRxVuMmrGV2xN/0APGdQYwLFky5w9naebSZ0OwAGp0G6/2Cg90rA==", - "dev": true - }, - "js-tokens": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", - "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", - "dev": true - }, - "jsesc": { - "version": "2.5.2", - "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz", - "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==", - "dev": true - }, - "json-parse-better-errors": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz", - "integrity": "sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw==", - "dev": true - }, - "json-parse-even-better-errors": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", - "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==", - "dev": true - }, - "json-schema-traverse": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", - "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", - "dev": true - }, - "json5": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.1.tgz", - "integrity": "sha512-1hqLFMSrGHRHxav9q9gNjJ5EXznIxGVO09xQRrwplcS8qs28pZ8s8hupZAmqDwZUmVZ2Qb2jnyPOWcDH8m8dlA==", - "dev": true - }, - "jsonfile": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz", - "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==", - "dev": true, - "requires": { - "graceful-fs": "^4.1.6", - "universalify": "^2.0.0" - } - }, - "kind-of": { - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", - "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==", - "dev": true - }, - "klona": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/klona/-/klona-2.0.5.tgz", - "integrity": "sha512-pJiBpiXMbt7dkzXe8Ghj/u4FfXOOa98fPW+bihOJ4SjnoijweJrNThJfd3ifXpXhREjpoF2mZVH1GfS9LV3kHQ==", - "dev": true - }, - "launch-editor": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/launch-editor/-/launch-editor-2.3.0.tgz", - "integrity": "sha512-3QrsCXejlWYHjBPFXTyGNhPj4rrQdB+5+r5r3wArpLH201aR+nWUgw/zKKkTmilCfY/sv6u8qo98pNvtg8LUTA==", - "dev": true, - "requires": { - "picocolors": "^1.0.0", - "shell-quote": "^1.6.1" - } - }, - "launch-editor-middleware": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/launch-editor-middleware/-/launch-editor-middleware-2.3.0.tgz", - "integrity": "sha512-GJR64trLdFFwCoL9DMn/d1SZX0OzTDPixu4mcfWTShQ4tIqCHCGvlg9fOEYQXyBlrSMQwylsJfUWncheShfV2w==", - "dev": true, - "requires": { - "launch-editor": "^2.3.0" - } - }, - "lilconfig": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-2.0.5.tgz", - "integrity": "sha512-xaYmXZtTHPAw5m+xLN8ab9C+3a8YmV3asNSPOATITbtwrfbwaLJj8h66H1WMIpALCkqsIzK3h7oQ+PdX+LQ9Eg==", - "dev": true - }, - "lines-and-columns": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", - "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==", - "dev": true - }, - "loader-runner": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/loader-runner/-/loader-runner-4.2.0.tgz", - "integrity": "sha512-92+huvxMvYlMzMt0iIOukcwYBFpkYJdpl2xsZ7LrlayO7E8SOv+JJUEK17B/dJIHAOLMfh2dZZ/Y18WgmGtYNw==", - "dev": true - }, - "loader-utils": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-2.0.2.tgz", - "integrity": "sha512-TM57VeHptv569d/GKh6TAYdzKblwDNiumOdkFnejjD0XwTH87K90w3O7AiJRqdQoXygvi1VQTJTLGhJl7WqA7A==", - "dev": true, - "requires": { - "big.js": "^5.2.2", - "emojis-list": "^3.0.0", - "json5": "^2.1.2" - } - }, - "locate-path": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", - "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", - "dev": true, - "requires": { - "p-locate": "^4.1.0" - } - }, - "lodash": { - "version": "4.17.21", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", - "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", - "dev": true - }, - "lodash.debounce": { - "version": "4.0.8", - "resolved": "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz", - "integrity": "sha1-gteb/zCmfEAF/9XiUVMArZyk168=", - "dev": true - }, - "lodash.defaultsdeep": { - "version": "4.6.1", - "resolved": "https://registry.npmjs.org/lodash.defaultsdeep/-/lodash.defaultsdeep-4.6.1.tgz", - "integrity": "sha512-3j8wdDzYuWO3lM3Reg03MuQR957t287Rpcxp1njpEa8oDrikb+FwGdW3n+FELh/A6qib6yPit0j/pv9G/yeAqA==", - "dev": true - }, - "lodash.kebabcase": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/lodash.kebabcase/-/lodash.kebabcase-4.1.1.tgz", - "integrity": "sha1-hImxyw0p/4gZXM7KRI/21swpXDY=", - "dev": true - }, - "lodash.mapvalues": { - "version": "4.6.0", - "resolved": "https://registry.npmjs.org/lodash.mapvalues/-/lodash.mapvalues-4.6.0.tgz", - "integrity": "sha1-G6+lAF3p3W9PJmaMMMo3IwzJaJw=", - "dev": true - }, - "lodash.memoize": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-4.1.2.tgz", - "integrity": "sha1-vMbEmkKihA7Zl/Mj6tpezRguC/4=", - "dev": true - }, - "lodash.uniq": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/lodash.uniq/-/lodash.uniq-4.5.0.tgz", - "integrity": "sha1-0CJTc662Uq3BvILklFM5qEJ1R3M=", - "dev": true - }, - "log-symbols": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz", - "integrity": "sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==", - "dev": true, - "requires": { - "chalk": "^4.1.0", - "is-unicode-supported": "^0.1.0" - }, - "dependencies": { - "ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "requires": { - "color-convert": "^2.0.1" - } - }, - "chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - } - }, - "color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "requires": { - "color-name": "~1.1.4" - } - }, - "color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true - }, - "supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "requires": { - "has-flag": "^4.0.0" - } - } - } - }, - "log-update": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/log-update/-/log-update-2.3.0.tgz", - "integrity": "sha1-iDKP19HOeTiykoN0bwsbwSayRwg=", - "dev": true, - "requires": { - "ansi-escapes": "^3.0.0", - "cli-cursor": "^2.0.0", - "wrap-ansi": "^3.0.1" - }, - "dependencies": { - "ansi-regex": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.1.tgz", - "integrity": "sha512-+O9Jct8wf++lXxxFc4hc8LsjaSq0HFzzL7cVsw8pRDIPdjKD2mT4ytDZlLuSBZ4cLKZFXIrMGO7DbQCtMJJMKw==", - "dev": true - }, - "cli-cursor": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-2.1.0.tgz", - "integrity": "sha1-s12sN2R5+sw+lHR9QdDQ9SOP/LU=", - "dev": true, - "requires": { - "restore-cursor": "^2.0.0" - } - }, - "is-fullwidth-code-point": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", - "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=", - "dev": true - }, - "mimic-fn": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-1.2.0.tgz", - "integrity": "sha512-jf84uxzwiuiIVKiOLpfYk7N46TSy8ubTonmneY9vrpHNAnp0QBt2BxWV9dO3/j+BoVAb+a5G6YDPW3M5HOdMWQ==", - "dev": true - }, - "onetime": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/onetime/-/onetime-2.0.1.tgz", - "integrity": "sha1-BnQoIw/WdEOyeUsiu6UotoZ5YtQ=", - "dev": true, - "requires": { - "mimic-fn": "^1.0.0" - } - }, - "restore-cursor": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-2.0.0.tgz", - "integrity": "sha1-n37ih/gv0ybU/RYpI9YhKe7g368=", - "dev": true, - "requires": { - "onetime": "^2.0.0", - "signal-exit": "^3.0.2" - } - }, - "string-width": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz", - "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==", - "dev": true, - "requires": { - "is-fullwidth-code-point": "^2.0.0", - "strip-ansi": "^4.0.0" - } - }, - "strip-ansi": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", - "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", - "dev": true, - "requires": { - "ansi-regex": "^3.0.0" - } - }, - "wrap-ansi": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-3.0.1.tgz", - "integrity": "sha1-KIoE2H7aXChuBg3+jxNc6NAH+Lo=", - "dev": true, - "requires": { - "string-width": "^2.1.1", - "strip-ansi": "^4.0.0" - } - } - } - }, - "lower-case": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/lower-case/-/lower-case-2.0.2.tgz", - "integrity": "sha512-7fm3l3NAF9WfN6W3JOmf5drwpVqX78JtoGJ3A6W0a6ZnldM41w2fV5D490psKFTpMds8TJse/eHLFFsNHHjHgg==", - "dev": true, - "requires": { - "tslib": "^2.0.3" - } - }, - "lru-cache": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", - "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", - "dev": true, - "requires": { - "yallist": "^4.0.0" - } - }, - "magic-string": { - "version": "0.25.9", - "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.25.9.tgz", - "integrity": "sha512-RmF0AsMzgt25qzqqLc1+MbHmhdx0ojF2Fvs4XnOqz2ZOBXzzkEwc/dJQZCYHAn7v1jbVOjAZfK8msRn4BxO4VQ==", - "requires": { - "sourcemap-codec": "^1.4.8" - } - }, - "make-dir": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz", - "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==", - "dev": true, - "requires": { - "semver": "^6.0.0" - } - }, - "mdn-data": { - "version": "2.0.14", - "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.14.tgz", - "integrity": "sha512-dn6wd0uw5GsdswPFfsgMp5NSB0/aDe6fK94YJV/AJDYXL6HVLWBsxeq7js7Ad+mU2K9LAlwpk6kN2D5mwCPVow==", - "dev": true - }, - "media-typer": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", - "integrity": "sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g=", - "dev": true - }, - "memfs": { - "version": "3.4.1", - "resolved": "https://registry.npmjs.org/memfs/-/memfs-3.4.1.tgz", - "integrity": "sha512-1c9VPVvW5P7I85c35zAdEr1TD5+F11IToIHIlrVIcflfnzPkJa0ZoYEoEdYDP8KgPFoSZ/opDrUsAoZWym3mtw==", - "dev": true, - "requires": { - "fs-monkey": "1.0.3" - } - }, - "merge-descriptors": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", - "integrity": "sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E=", - "dev": true - }, - "merge-source-map": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/merge-source-map/-/merge-source-map-1.1.0.tgz", - "integrity": "sha512-Qkcp7P2ygktpMPh2mCQZaf3jhN6D3Z/qVZHSdWvQ+2Ef5HgRAPBO57A77+ENm0CPx2+1Ce/MYKi3ymqdfuqibw==", - "dev": true, - "requires": { - "source-map": "^0.6.1" - } - }, - "merge-stream": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", - "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", - "dev": true - }, - "merge2": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", - "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", - "dev": true - }, - "methods": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", - "integrity": "sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4=", - "dev": true - }, - "micromatch": { - "version": "4.0.5", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz", - "integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==", - "dev": true, - "requires": { - "braces": "^3.0.2", - "picomatch": "^2.3.1" - } - }, - "mime": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", - "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", - "dev": true - }, - "mime-db": { - "version": "1.52.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", - "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", - "dev": true - }, - "mime-types": { - "version": "2.1.35", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", - "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", - "dev": true, - "requires": { - "mime-db": "1.52.0" - } - }, - "mimic-fn": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", - "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", - "dev": true - }, - "mini-css-extract-plugin": { - "version": "2.6.0", - "resolved": "https://registry.npmjs.org/mini-css-extract-plugin/-/mini-css-extract-plugin-2.6.0.tgz", - "integrity": "sha512-ndG8nxCEnAemsg4FSgS+yNyHKgkTB4nPKqCOgh65j3/30qqC5RaSQQXMm++Y6sb6E1zRSxPkztj9fqxhS1Eo6w==", - "dev": true, - "requires": { - "schema-utils": "^4.0.0" - }, - "dependencies": { - "ajv": { - "version": "8.11.0", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.11.0.tgz", - "integrity": "sha512-wGgprdCvMalC0BztXvitD2hC04YffAvtsUn93JbGXYLAtCUO4xd17mCCZQxUOItiBwZvJScWo8NIvQMQ71rdpg==", - "dev": true, - "requires": { - "fast-deep-equal": "^3.1.1", - "json-schema-traverse": "^1.0.0", - "require-from-string": "^2.0.2", - "uri-js": "^4.2.2" - } - }, - "ajv-keywords": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-5.1.0.tgz", - "integrity": "sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw==", - "dev": true, - "requires": { - "fast-deep-equal": "^3.1.3" - } - }, - "json-schema-traverse": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", - "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", - "dev": true - }, - "schema-utils": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.0.0.tgz", - "integrity": "sha512-1edyXKgh6XnJsJSQ8mKWXnN/BVaIbFMLpouRUrXgVq7WYne5kw3MW7UPhO44uRXQSIpTSXoJbmrR2X0w9kUTyg==", - "dev": true, - "requires": { - "@types/json-schema": "^7.0.9", - "ajv": "^8.8.0", - "ajv-formats": "^2.1.1", - "ajv-keywords": "^5.0.0" - } - } - } - }, - "minimalistic-assert": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz", - "integrity": "sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==", - "dev": true - }, - "minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "dev": true, - "requires": { - "brace-expansion": "^1.1.7" - } - }, - "minimist": { - "version": "1.2.6", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.6.tgz", - "integrity": "sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q==", - "dev": true - }, - "minipass": { - "version": "3.1.6", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.1.6.tgz", - "integrity": "sha512-rty5kpw9/z8SX9dmxblFA6edItUmwJgMeYDZRrwlIVN27i8gysGbznJwUggw2V/FVqFSDdWy040ZPS811DYAqQ==", - "dev": true, - "requires": { - "yallist": "^4.0.0" - } - }, - "mkdirp": { - "version": "0.5.6", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz", - "integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==", - "dev": true, - "requires": { - "minimist": "^1.2.6" - } - }, - "module-alias": { - "version": "2.2.2", - "resolved": "https://registry.npmjs.org/module-alias/-/module-alias-2.2.2.tgz", - "integrity": "sha512-A/78XjoX2EmNvppVWEhM2oGk3x4lLxnkEA4jTbaK97QKSDjkIoOsKQlfylt/d3kKKi596Qy3NP5XrXJ6fZIC9Q==", - "dev": true - }, - "mrmime": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/mrmime/-/mrmime-1.0.0.tgz", - "integrity": "sha512-a70zx7zFfVO7XpnQ2IX1Myh9yY4UYvfld/dikWRnsXxbyvMcfz+u6UfgNAtH+k2QqtJuzVpv6eLTx1G2+WKZbQ==", - "dev": true - }, - "ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "dev": true - }, - "multicast-dns": { - "version": "7.2.4", - "resolved": "https://registry.npmjs.org/multicast-dns/-/multicast-dns-7.2.4.tgz", - "integrity": "sha512-XkCYOU+rr2Ft3LI6w4ye51M3VK31qJXFIxu0XLw169PtKG0Zx47OrXeVW/GCYOfpC9s1yyyf1S+L8/4LY0J9Zw==", - "dev": true, - "requires": { - "dns-packet": "^5.2.2", - "thunky": "^1.0.2" - } - }, - "mz": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/mz/-/mz-2.7.0.tgz", - "integrity": "sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==", - "dev": true, - "requires": { - "any-promise": "^1.0.0", - "object-assign": "^4.0.1", - "thenify-all": "^1.0.0" - } - }, - "nanoid": { - "version": "3.3.2", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.2.tgz", - "integrity": "sha512-CuHBogktKwpm5g2sRgv83jEy2ijFzBwMoYA60orPDR7ynsLijJDqgsi4RDGj3OJpy3Ieb+LYwiRmIOGyytgITA==" - }, - "negotiator": { - "version": "0.6.3", - "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", - "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==", - "dev": true - }, - "neo-async": { - "version": "2.6.2", - "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz", - "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==", - "dev": true - }, - "nice-try": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/nice-try/-/nice-try-1.0.5.tgz", - "integrity": "sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==", - "dev": true - }, - "no-case": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/no-case/-/no-case-3.0.4.tgz", - "integrity": "sha512-fgAN3jGAh+RoxUGZHTSOLJIqUc2wmoBwGR4tbpNAKmmovFoWq0OdRkb0VkldReO2a2iBT/OEulG9XSUc10r3zg==", - "dev": true, - "requires": { - "lower-case": "^2.0.2", - "tslib": "^2.0.3" - } - }, - "node-fetch": { - "version": "2.6.7", - "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.7.tgz", - "integrity": "sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ==", - "dev": true, - "requires": { - "whatwg-url": "^5.0.0" - } - }, - "node-forge": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/node-forge/-/node-forge-1.3.1.tgz", - "integrity": "sha512-dPEtOeMvF9VMcYV/1Wb8CPoVAXtp6MKMlcbAt4ddqmGqUJ6fQZFXkNZNkNlfevtNkGtaSoXf/vNNNSvgrdXwtA==", - "dev": true - }, - "node-releases": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.2.tgz", - "integrity": "sha512-XxYDdcQ6eKqp/YjI+tb2C5WM2LgjnZrfYg4vgQt49EK268b6gYCHsBLrK2qvJo4FmCtqmKezb0WZFK4fkrZNsg==", - "dev": true - }, - "normalize-package-data": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz", - "integrity": "sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==", - "dev": true, - "requires": { - "hosted-git-info": "^2.1.4", - "resolve": "^1.10.0", - "semver": "2 || 3 || 4 || 5", - "validate-npm-package-license": "^3.0.1" - }, - "dependencies": { - "semver": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", - "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", - "dev": true - } - } - }, - "normalize-path": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", - "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", - "dev": true - }, - "normalize-range": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/normalize-range/-/normalize-range-0.1.2.tgz", - "integrity": "sha1-LRDAa9/TEuqXd2laTShDlFa3WUI=", - "dev": true - }, - "normalize-url": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-6.1.0.tgz", - "integrity": "sha512-DlL+XwOy3NxAQ8xuC0okPgK46iuVNAK01YN7RueYBqqFeGsBjV9XmCAzAdgt+667bCl5kPh9EqKKDwnaPG1I7A==", - "dev": true - }, - "npm-run-path": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-2.0.2.tgz", - "integrity": "sha1-NakjLfo11wZ7TLLd8jV7GHFTbF8=", - "dev": true, - "requires": { - "path-key": "^2.0.0" - } - }, - "nth-check": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-2.0.1.tgz", - "integrity": "sha512-it1vE95zF6dTT9lBsYbxvqh0Soy4SPowchj0UBGj/V6cTPnXXtQOPUbhZ6CmGzAD/rW22LQK6E96pcdJXk4A4w==", - "dev": true, - "requires": { - "boolbase": "^1.0.0" - } - }, - "object-assign": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", - "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=", - "dev": true - }, - "object-keys": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", - "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", - "dev": true - }, - "object.assign": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.2.tgz", - "integrity": "sha512-ixT2L5THXsApyiUPYKmW+2EHpXXe5Ii3M+f4e+aJFAHao5amFRW6J0OO6c/LU8Be47utCx2GL89hxGB6XSmKuQ==", - "dev": true, - "requires": { - "call-bind": "^1.0.0", - "define-properties": "^1.1.3", - "has-symbols": "^1.0.1", - "object-keys": "^1.1.1" - } - }, - "obuf": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/obuf/-/obuf-1.1.2.tgz", - "integrity": "sha512-PX1wu0AmAdPqOL1mWhqmlOd8kOIZQwGZw6rh7uby9fTc5lhaOWFLX3I6R1hrF9k3zUY40e6igsLGkDXK92LJNg==", - "dev": true - }, - "on-finished": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz", - "integrity": "sha1-IPEzZIGwg811M3mSoWlxqi2QaUc=", - "dev": true, - "requires": { - "ee-first": "1.1.1" - } - }, - "on-headers": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/on-headers/-/on-headers-1.0.2.tgz", - "integrity": "sha512-pZAE+FJLoyITytdqK0U5s+FIpjN0JP3OzFi/u8Rx+EV5/W+JTWGXG8xFzevE7AjBfDqHv/8vL8qQsIhHnqRkrA==", - "dev": true - }, - "once": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", - "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", - "dev": true, - "requires": { - "wrappy": "1" - } - }, - "onetime": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", - "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", - "dev": true, - "requires": { - "mimic-fn": "^2.1.0" - } - }, - "open": { - "version": "8.4.0", - "resolved": "https://registry.npmjs.org/open/-/open-8.4.0.tgz", - "integrity": "sha512-XgFPPM+B28FtCCgSb9I+s9szOC1vZRSwgWsRUA5ylIxRTgKozqjOCrVOqGsYABPYK5qnfqClxZTFBa8PKt2v6Q==", - "dev": true, - "requires": { - "define-lazy-prop": "^2.0.0", - "is-docker": "^2.1.1", - "is-wsl": "^2.2.0" - } - }, - "opener": { - "version": "1.5.2", - "resolved": "https://registry.npmjs.org/opener/-/opener-1.5.2.tgz", - "integrity": "sha512-ur5UIdyw5Y7yEj9wLzhqXiy6GZ3Mwx0yGI+5sMn2r0N0v3cKJvUmFH5yPP+WXh9e0xfyzyJX95D8l088DNFj7A==", - "dev": true - }, - "ora": { - "version": "5.4.1", - "resolved": "https://registry.npmjs.org/ora/-/ora-5.4.1.tgz", - "integrity": "sha512-5b6Y85tPxZZ7QytO+BQzysW31HJku27cRIlkbAXaNx+BdcVi+LlRFmVXzeF6a7JCwJpyw5c4b+YSVImQIrBpuQ==", - "dev": true, - "requires": { - "bl": "^4.1.0", - "chalk": "^4.1.0", - "cli-cursor": "^3.1.0", - "cli-spinners": "^2.5.0", - "is-interactive": "^1.0.0", - "is-unicode-supported": "^0.1.0", - "log-symbols": "^4.1.0", - "strip-ansi": "^6.0.0", - "wcwidth": "^1.0.1" - }, - "dependencies": { - "ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "requires": { - "color-convert": "^2.0.1" - } - }, - "chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - } - }, - "color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "requires": { - "color-name": "~1.1.4" - } - }, - "color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true - }, - "supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "requires": { - "has-flag": "^4.0.0" - } - } - } - }, - "p-finally": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz", - "integrity": "sha1-P7z7FbiZpEEjs0ttzBi3JDNqLK4=", - "dev": true - }, - "p-limit": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", - "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", - "dev": true, - "requires": { - "p-try": "^2.0.0" - } - }, - "p-locate": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", - "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", - "dev": true, - "requires": { - "p-limit": "^2.2.0" - } - }, - "p-retry": { - "version": "4.6.1", - "resolved": "https://registry.npmjs.org/p-retry/-/p-retry-4.6.1.tgz", - "integrity": "sha512-e2xXGNhZOZ0lfgR9kL34iGlU8N/KO0xZnQxVEwdeOvpqNDQfdnxIYizvWtK8RglUa3bGqI8g0R/BdfzLMxRkiA==", - "dev": true, - "requires": { - "@types/retry": "^0.12.0", - "retry": "^0.13.1" - } - }, - "p-try": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", - "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", - "dev": true - }, - "param-case": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/param-case/-/param-case-3.0.4.tgz", - "integrity": "sha512-RXlj7zCYokReqWpOPH9oYivUzLYZ5vAPIfEmCTNViosC78F8F0H9y7T7gG2M39ymgutxF5gcFEsyZQSph9Bp3A==", - "dev": true, - "requires": { - "dot-case": "^3.0.4", - "tslib": "^2.0.3" - } - }, - "parent-module": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", - "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", - "dev": true, - "requires": { - "callsites": "^3.0.0" - } - }, - "parse-json": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", - "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==", - "dev": true, - "requires": { - "@babel/code-frame": "^7.0.0", - "error-ex": "^1.3.1", - "json-parse-even-better-errors": "^2.3.0", - "lines-and-columns": "^1.1.6" - } - }, - "parse5": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/parse5/-/parse5-5.1.1.tgz", - "integrity": "sha512-ugq4DFI0Ptb+WWjAdOK16+u/nHfiIrcE+sh8kZMaM0WllQKLI9rOUq6c2b7cwPkXdzfQESqvoqK6ug7U/Yyzug==", - "dev": true - }, - "parse5-htmlparser2-tree-adapter": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/parse5-htmlparser2-tree-adapter/-/parse5-htmlparser2-tree-adapter-6.0.1.tgz", - "integrity": "sha512-qPuWvbLgvDGilKc5BoicRovlT4MtYT6JfJyBOMDsKoiT+GiuP5qyrPCnR9HcPECIJJmZh5jRndyNThnhhb/vlA==", - "dev": true, - "requires": { - "parse5": "^6.0.1" - }, - "dependencies": { - "parse5": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/parse5/-/parse5-6.0.1.tgz", - "integrity": "sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw==", - "dev": true - } - } - }, - "parseurl": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", - "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", - "dev": true - }, - "pascal-case": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/pascal-case/-/pascal-case-3.1.2.tgz", - "integrity": "sha512-uWlGT3YSnK9x3BQJaOdcZwrnV6hPpd8jFH1/ucpiLRPh/2zCVJKS19E4GvYHvaCcACn3foXZ0cLB9Wrx1KGe5g==", - "dev": true, - "requires": { - "no-case": "^3.0.4", - "tslib": "^2.0.3" - } - }, - "path-exists": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", - "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", - "dev": true - }, - "path-is-absolute": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", - "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", - "dev": true - }, - "path-key": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz", - "integrity": "sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A=", - "dev": true - }, - "path-parse": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", - "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", - "dev": true - }, - "path-to-regexp": { - "version": "0.1.7", - "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", - "integrity": "sha1-32BBeABfUi8V60SQ5yR6G/qmf4w=", - "dev": true - }, - "path-type": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", - "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", - "dev": true - }, - "picocolors": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", - "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==" - }, - "picomatch": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", - "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", - "dev": true - }, - "pkg-dir": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz", - "integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==", - "dev": true, - "requires": { - "find-up": "^4.0.0" - } - }, - "portfinder": { - "version": "1.0.28", - "resolved": "https://registry.npmjs.org/portfinder/-/portfinder-1.0.28.tgz", - "integrity": "sha512-Se+2isanIcEqf2XMHjyUKskczxbPH7dQnlMjXX6+dybayyHvAf/TCgyMRlzf/B6QDhAEFOGes0pzRo3by4AbMA==", - "dev": true, - "requires": { - "async": "^2.6.2", - "debug": "^3.1.1", - "mkdirp": "^0.5.5" - }, - "dependencies": { - "debug": { - "version": "3.2.7", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", - "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", - "dev": true, - "requires": { - "ms": "^2.1.1" - } - } - } - }, - "postcss": { - "version": "8.4.12", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.12.tgz", - "integrity": "sha512-lg6eITwYe9v6Hr5CncVbK70SoioNQIq81nsaG86ev5hAidQvmOeETBqs7jm43K2F5/Ley3ytDtriImV6TpNiSg==", - "requires": { - "nanoid": "^3.3.1", - "picocolors": "^1.0.0", - "source-map-js": "^1.0.2" - } - }, - "postcss-calc": { - "version": "8.2.4", - "resolved": "https://registry.npmjs.org/postcss-calc/-/postcss-calc-8.2.4.tgz", - "integrity": "sha512-SmWMSJmB8MRnnULldx0lQIyhSNvuDl9HfrZkaqqE/WHAhToYsAvDq+yAsA/kIyINDszOp3Rh0GFoNuH5Ypsm3Q==", - "dev": true, - "requires": { - "postcss-selector-parser": "^6.0.9", - "postcss-value-parser": "^4.2.0" - } - }, - "postcss-colormin": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/postcss-colormin/-/postcss-colormin-5.3.0.tgz", - "integrity": "sha512-WdDO4gOFG2Z8n4P8TWBpshnL3JpmNmJwdnfP2gbk2qBA8PWwOYcmjmI/t3CmMeL72a7Hkd+x/Mg9O2/0rD54Pg==", - "dev": true, - "requires": { - "browserslist": "^4.16.6", - "caniuse-api": "^3.0.0", - "colord": "^2.9.1", - "postcss-value-parser": "^4.2.0" - } - }, - "postcss-convert-values": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/postcss-convert-values/-/postcss-convert-values-5.1.0.tgz", - "integrity": "sha512-GkyPbZEYJiWtQB0KZ0X6qusqFHUepguBCNFi9t5JJc7I2OTXG7C0twbTLvCfaKOLl3rSXmpAwV7W5txd91V84g==", - "dev": true, - "requires": { - "postcss-value-parser": "^4.2.0" - } - }, - "postcss-discard-comments": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/postcss-discard-comments/-/postcss-discard-comments-5.1.1.tgz", - "integrity": "sha512-5JscyFmvkUxz/5/+TB3QTTT9Gi9jHkcn8dcmmuN68JQcv3aQg4y88yEHHhwFB52l/NkaJ43O0dbksGMAo49nfQ==", - "dev": true - }, - "postcss-discard-duplicates": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/postcss-discard-duplicates/-/postcss-discard-duplicates-5.1.0.tgz", - "integrity": "sha512-zmX3IoSI2aoenxHV6C7plngHWWhUOV3sP1T8y2ifzxzbtnuhk1EdPwm0S1bIUNaJ2eNbWeGLEwzw8huPD67aQw==", - "dev": true - }, - "postcss-discard-empty": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/postcss-discard-empty/-/postcss-discard-empty-5.1.1.tgz", - "integrity": "sha512-zPz4WljiSuLWsI0ir4Mcnr4qQQ5e1Ukc3i7UfE2XcrwKK2LIPIqE5jxMRxO6GbI3cv//ztXDsXwEWT3BHOGh3A==", - "dev": true - }, - "postcss-discard-overridden": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/postcss-discard-overridden/-/postcss-discard-overridden-5.1.0.tgz", - "integrity": "sha512-21nOL7RqWR1kasIVdKs8HNqQJhFxLsyRfAnUDm4Fe4t4mCWL9OJiHvlHPjcd8zc5Myu89b/7wZDnOSjFgeWRtw==", - "dev": true - }, - "postcss-loader": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/postcss-loader/-/postcss-loader-6.2.1.tgz", - "integrity": "sha512-WbbYpmAaKcux/P66bZ40bpWsBucjx/TTgVVzRZ9yUO8yQfVBlameJ0ZGVaPfH64hNSBh63a+ICP5nqOpBA0w+Q==", - "dev": true, - "requires": { - "cosmiconfig": "^7.0.0", - "klona": "^2.0.5", - "semver": "^7.3.5" - }, - "dependencies": { - "semver": { - "version": "7.3.5", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz", - "integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==", - "dev": true, - "requires": { - "lru-cache": "^6.0.0" - } - } - } - }, - "postcss-merge-longhand": { - "version": "5.1.4", - "resolved": "https://registry.npmjs.org/postcss-merge-longhand/-/postcss-merge-longhand-5.1.4.tgz", - "integrity": "sha512-hbqRRqYfmXoGpzYKeW0/NCZhvNyQIlQeWVSao5iKWdyx7skLvCfQFGIUsP9NUs3dSbPac2IC4Go85/zG+7MlmA==", - "dev": true, - "requires": { - "postcss-value-parser": "^4.2.0", - "stylehacks": "^5.1.0" - } - }, - "postcss-merge-rules": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/postcss-merge-rules/-/postcss-merge-rules-5.1.1.tgz", - "integrity": "sha512-8wv8q2cXjEuCcgpIB1Xx1pIy8/rhMPIQqYKNzEdyx37m6gpq83mQQdCxgIkFgliyEnKvdwJf/C61vN4tQDq4Ww==", - "dev": true, - "requires": { - "browserslist": "^4.16.6", - "caniuse-api": "^3.0.0", - "cssnano-utils": "^3.1.0", - "postcss-selector-parser": "^6.0.5" - } - }, - "postcss-minify-font-values": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/postcss-minify-font-values/-/postcss-minify-font-values-5.1.0.tgz", - "integrity": "sha512-el3mYTgx13ZAPPirSVsHqFzl+BBBDrXvbySvPGFnQcTI4iNslrPaFq4muTkLZmKlGk4gyFAYUBMH30+HurREyA==", - "dev": true, - "requires": { - "postcss-value-parser": "^4.2.0" - } - }, - "postcss-minify-gradients": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/postcss-minify-gradients/-/postcss-minify-gradients-5.1.1.tgz", - "integrity": "sha512-VGvXMTpCEo4qHTNSa9A0a3D+dxGFZCYwR6Jokk+/3oB6flu2/PnPXAh2x7x52EkY5xlIHLm+Le8tJxe/7TNhzw==", - "dev": true, - "requires": { - "colord": "^2.9.1", - "cssnano-utils": "^3.1.0", - "postcss-value-parser": "^4.2.0" - } - }, - "postcss-minify-params": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/postcss-minify-params/-/postcss-minify-params-5.1.2.tgz", - "integrity": "sha512-aEP+p71S/urY48HWaRHasyx4WHQJyOYaKpQ6eXl8k0kxg66Wt/30VR6/woh8THgcpRbonJD5IeD+CzNhPi1L8g==", - "dev": true, - "requires": { - "browserslist": "^4.16.6", - "cssnano-utils": "^3.1.0", - "postcss-value-parser": "^4.2.0" - } - }, - "postcss-minify-selectors": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/postcss-minify-selectors/-/postcss-minify-selectors-5.2.0.tgz", - "integrity": "sha512-vYxvHkW+iULstA+ctVNx0VoRAR4THQQRkG77o0oa4/mBS0OzGvvzLIvHDv/nNEM0crzN2WIyFU5X7wZhaUK3RA==", - "dev": true, - "requires": { - "postcss-selector-parser": "^6.0.5" - } - }, - "postcss-modules-extract-imports": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/postcss-modules-extract-imports/-/postcss-modules-extract-imports-3.0.0.tgz", - "integrity": "sha512-bdHleFnP3kZ4NYDhuGlVK+CMrQ/pqUm8bx/oGL93K6gVwiclvX5x0n76fYMKuIGKzlABOy13zsvqjb0f92TEXw==", - "dev": true - }, - "postcss-modules-local-by-default": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/postcss-modules-local-by-default/-/postcss-modules-local-by-default-4.0.0.tgz", - "integrity": "sha512-sT7ihtmGSF9yhm6ggikHdV0hlziDTX7oFoXtuVWeDd3hHObNkcHRo9V3yg7vCAY7cONyxJC/XXCmmiHHcvX7bQ==", - "dev": true, - "requires": { - "icss-utils": "^5.0.0", - "postcss-selector-parser": "^6.0.2", - "postcss-value-parser": "^4.1.0" - } - }, - "postcss-modules-scope": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/postcss-modules-scope/-/postcss-modules-scope-3.0.0.tgz", - "integrity": "sha512-hncihwFA2yPath8oZ15PZqvWGkWf+XUfQgUGamS4LqoP1anQLOsOJw0vr7J7IwLpoY9fatA2qiGUGmuZL0Iqlg==", - "dev": true, - "requires": { - "postcss-selector-parser": "^6.0.4" - } - }, - "postcss-modules-values": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/postcss-modules-values/-/postcss-modules-values-4.0.0.tgz", - "integrity": "sha512-RDxHkAiEGI78gS2ofyvCsu7iycRv7oqw5xMWn9iMoR0N/7mf9D50ecQqUo5BZ9Zh2vH4bCUR/ktCqbB9m8vJjQ==", - "dev": true, - "requires": { - "icss-utils": "^5.0.0" - } - }, - "postcss-normalize-charset": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/postcss-normalize-charset/-/postcss-normalize-charset-5.1.0.tgz", - "integrity": "sha512-mSgUJ+pd/ldRGVx26p2wz9dNZ7ji6Pn8VWBajMXFf8jk7vUoSrZ2lt/wZR7DtlZYKesmZI680qjr2CeFF2fbUg==", - "dev": true - }, - "postcss-normalize-display-values": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/postcss-normalize-display-values/-/postcss-normalize-display-values-5.1.0.tgz", - "integrity": "sha512-WP4KIM4o2dazQXWmFaqMmcvsKmhdINFblgSeRgn8BJ6vxaMyaJkwAzpPpuvSIoG/rmX3M+IrRZEz2H0glrQNEA==", - "dev": true, - "requires": { - "postcss-value-parser": "^4.2.0" - } - }, - "postcss-normalize-positions": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/postcss-normalize-positions/-/postcss-normalize-positions-5.1.0.tgz", - "integrity": "sha512-8gmItgA4H5xiUxgN/3TVvXRoJxkAWLW6f/KKhdsH03atg0cB8ilXnrB5PpSshwVu/dD2ZsRFQcR1OEmSBDAgcQ==", - "dev": true, - "requires": { - "postcss-value-parser": "^4.2.0" - } - }, - "postcss-normalize-repeat-style": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/postcss-normalize-repeat-style/-/postcss-normalize-repeat-style-5.1.0.tgz", - "integrity": "sha512-IR3uBjc+7mcWGL6CtniKNQ4Rr5fTxwkaDHwMBDGGs1x9IVRkYIT/M4NelZWkAOBdV6v3Z9S46zqaKGlyzHSchw==", - "dev": true, - "requires": { - "postcss-value-parser": "^4.2.0" - } - }, - "postcss-normalize-string": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/postcss-normalize-string/-/postcss-normalize-string-5.1.0.tgz", - "integrity": "sha512-oYiIJOf4T9T1N4i+abeIc7Vgm/xPCGih4bZz5Nm0/ARVJ7K6xrDlLwvwqOydvyL3RHNf8qZk6vo3aatiw/go3w==", - "dev": true, - "requires": { - "postcss-value-parser": "^4.2.0" - } - }, - "postcss-normalize-timing-functions": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/postcss-normalize-timing-functions/-/postcss-normalize-timing-functions-5.1.0.tgz", - "integrity": "sha512-DOEkzJ4SAXv5xkHl0Wa9cZLF3WCBhF3o1SKVxKQAa+0pYKlueTpCgvkFAHfk+Y64ezX9+nITGrDZeVGgITJXjg==", - "dev": true, - "requires": { - "postcss-value-parser": "^4.2.0" - } - }, - "postcss-normalize-unicode": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/postcss-normalize-unicode/-/postcss-normalize-unicode-5.1.0.tgz", - "integrity": "sha512-J6M3MizAAZ2dOdSjy2caayJLQT8E8K9XjLce8AUQMwOrCvjCHv24aLC/Lps1R1ylOfol5VIDMaM/Lo9NGlk1SQ==", - "dev": true, - "requires": { - "browserslist": "^4.16.6", - "postcss-value-parser": "^4.2.0" - } - }, - "postcss-normalize-url": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/postcss-normalize-url/-/postcss-normalize-url-5.1.0.tgz", - "integrity": "sha512-5upGeDO+PVthOxSmds43ZeMeZfKH+/DKgGRD7TElkkyS46JXAUhMzIKiCa7BabPeIy3AQcTkXwVVN7DbqsiCew==", - "dev": true, - "requires": { - "normalize-url": "^6.0.1", - "postcss-value-parser": "^4.2.0" - } - }, - "postcss-normalize-whitespace": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/postcss-normalize-whitespace/-/postcss-normalize-whitespace-5.1.1.tgz", - "integrity": "sha512-83ZJ4t3NUDETIHTa3uEg6asWjSBYL5EdkVB0sDncx9ERzOKBVJIUeDO9RyA9Zwtig8El1d79HBp0JEi8wvGQnA==", - "dev": true, - "requires": { - "postcss-value-parser": "^4.2.0" - } - }, - "postcss-ordered-values": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/postcss-ordered-values/-/postcss-ordered-values-5.1.1.tgz", - "integrity": "sha512-7lxgXF0NaoMIgyihL/2boNAEZKiW0+HkMhdKMTD93CjW8TdCy2hSdj8lsAo+uwm7EDG16Da2Jdmtqpedl0cMfw==", - "dev": true, - "requires": { - "cssnano-utils": "^3.1.0", - "postcss-value-parser": "^4.2.0" - } - }, - "postcss-reduce-initial": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/postcss-reduce-initial/-/postcss-reduce-initial-5.1.0.tgz", - "integrity": "sha512-5OgTUviz0aeH6MtBjHfbr57tml13PuedK/Ecg8szzd4XRMbYxH4572JFG067z+FqBIf6Zp/d+0581glkvvWMFw==", - "dev": true, - "requires": { - "browserslist": "^4.16.6", - "caniuse-api": "^3.0.0" - } - }, - "postcss-reduce-transforms": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/postcss-reduce-transforms/-/postcss-reduce-transforms-5.1.0.tgz", - "integrity": "sha512-2fbdbmgir5AvpW9RLtdONx1QoYG2/EtqpNQbFASDlixBbAYuTcJ0dECwlqNqH7VbaUnEnh8SrxOe2sRIn24XyQ==", - "dev": true, - "requires": { - "postcss-value-parser": "^4.2.0" - } - }, - "postcss-selector-parser": { - "version": "6.0.10", - "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.0.10.tgz", - "integrity": "sha512-IQ7TZdoaqbT+LCpShg46jnZVlhWD2w6iQYAcYXfHARZ7X1t/UGhhceQDs5X0cGqKvYlHNOuv7Oa1xmb0oQuA3w==", - "dev": true, - "requires": { - "cssesc": "^3.0.0", - "util-deprecate": "^1.0.2" - } - }, - "postcss-svgo": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/postcss-svgo/-/postcss-svgo-5.1.0.tgz", - "integrity": "sha512-D75KsH1zm5ZrHyxPakAxJWtkyXew5qwS70v56exwvw542d9CRtTo78K0WeFxZB4G7JXKKMbEZtZayTGdIky/eA==", - "dev": true, - "requires": { - "postcss-value-parser": "^4.2.0", - "svgo": "^2.7.0" - } - }, - "postcss-unique-selectors": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/postcss-unique-selectors/-/postcss-unique-selectors-5.1.1.tgz", - "integrity": "sha512-5JiODlELrz8L2HwxfPnhOWZYWDxVHWL83ufOv84NrcgipI7TaeRsatAhK4Tr2/ZiYldpK/wBvw5BD3qfaK96GA==", - "dev": true, - "requires": { - "postcss-selector-parser": "^6.0.5" - } - }, - "postcss-value-parser": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz", - "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==", - "dev": true - }, - "prettier": { - "version": "2.6.2", - "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.6.2.tgz", - "integrity": "sha512-PkUpF+qoXTqhOeWL9fu7As8LXsIUZ1WYaJiY/a7McAQzxjk82OF0tibkFXVCDImZtWxbvojFjerkiLb0/q8mew==", - "dev": true, - "optional": true - }, - "pretty-error": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/pretty-error/-/pretty-error-4.0.0.tgz", - "integrity": "sha512-AoJ5YMAcXKYxKhuJGdcvse+Voc6v1RgnsR3nWcYU7q4t6z0Q6T86sv5Zq8VIRbOWWFpvdGE83LtdSMNd+6Y0xw==", - "dev": true, - "requires": { - "lodash": "^4.17.20", - "renderkid": "^3.0.0" - } - }, - "process-nextick-args": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", - "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==", - "dev": true - }, - "progress-webpack-plugin": { - "version": "1.0.16", - "resolved": "https://registry.npmjs.org/progress-webpack-plugin/-/progress-webpack-plugin-1.0.16.tgz", - "integrity": "sha512-sdiHuuKOzELcBANHfrupYo+r99iPRyOnw15qX+rNlVUqXGfjXdH4IgxriKwG1kNJwVswKQHMdj1hYZMcb9jFaA==", - "dev": true, - "requires": { - "chalk": "^2.1.0", - "figures": "^2.0.0", - "log-update": "^2.3.0" - } - }, - "proxy-addr": { - "version": "2.0.7", - "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", - "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==", - "dev": true, - "requires": { - "forwarded": "0.2.0", - "ipaddr.js": "1.9.1" - }, - "dependencies": { - "ipaddr.js": { - "version": "1.9.1", - "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", - "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==", - "dev": true - } - } - }, - "pseudomap": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/pseudomap/-/pseudomap-1.0.2.tgz", - "integrity": "sha1-8FKijacOYYkX7wqKw0wa5aaChrM=", - "dev": true - }, - "pump": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", - "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", - "dev": true, - "requires": { - "end-of-stream": "^1.1.0", - "once": "^1.3.1" - } - }, - "punycode": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", - "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==", - "dev": true - }, - "qs": { - "version": "6.9.7", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.9.7.tgz", - "integrity": "sha512-IhMFgUmuNpyRfxA90umL7ByLlgRXu6tIfKPpF5TmcfRLlLCckfP/g3IQmju6jjpu+Hh8rA+2p6A27ZSPOOHdKw==", - "dev": true - }, - "queue-microtask": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", - "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", - "dev": true - }, - "randombytes": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", - "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", - "dev": true, - "requires": { - "safe-buffer": "^5.1.0" - } - }, - "range-parser": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", - "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==", - "dev": true - }, - "raw-body": { - "version": "2.4.3", - "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.4.3.tgz", - "integrity": "sha512-UlTNLIcu0uzb4D2f4WltY6cVjLi+/jEN4lgEUj3E04tpMDpUlkBo/eSn6zou9hum2VMNpCCUone0O0WeJim07g==", - "dev": true, - "requires": { - "bytes": "3.1.2", - "http-errors": "1.8.1", - "iconv-lite": "0.4.24", - "unpipe": "1.0.0" - }, - "dependencies": { - "bytes": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", - "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", - "dev": true - } - } - }, - "read-pkg": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-5.2.0.tgz", - "integrity": "sha512-Ug69mNOpfvKDAc2Q8DRpMjjzdtrnv9HcSMX+4VsZxD1aZ6ZzrIE7rlzXBtWTyhULSMKg076AW6WR5iZpD0JiOg==", - "dev": true, - "requires": { - "@types/normalize-package-data": "^2.4.0", - "normalize-package-data": "^2.5.0", - "parse-json": "^5.0.0", - "type-fest": "^0.6.0" - }, - "dependencies": { - "type-fest": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.6.0.tgz", - "integrity": "sha512-q+MB8nYR1KDLrgr4G5yemftpMC7/QLqVndBmEEdqzmNj5dcFOO4Oo8qlwZE3ULT3+Zim1F8Kq4cBnikNhlCMlg==", - "dev": true - } - } - }, - "read-pkg-up": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-7.0.1.tgz", - "integrity": "sha512-zK0TB7Xd6JpCLmlLmufqykGE+/TlOePD6qKClNW7hHDKFh/J7/7gCWGR7joEQEW1bKq3a3yUZSObOoWLFQ4ohg==", - "dev": true, - "requires": { - "find-up": "^4.1.0", - "read-pkg": "^5.2.0", - "type-fest": "^0.8.1" - }, - "dependencies": { - "type-fest": { - "version": "0.8.1", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.8.1.tgz", - "integrity": "sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==", - "dev": true - } - } - }, - "readable-stream": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", - "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", - "dev": true, - "requires": { - "inherits": "^2.0.3", - "string_decoder": "^1.1.1", - "util-deprecate": "^1.0.1" - } - }, - "readdirp": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", - "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", - "dev": true, - "requires": { - "picomatch": "^2.2.1" - } - }, - "regenerate": { - "version": "1.4.2", - "resolved": "https://registry.npmjs.org/regenerate/-/regenerate-1.4.2.tgz", - "integrity": "sha512-zrceR/XhGYU/d/opr2EKO7aRHUeiBI8qjtfHqADTwZd6Szfy16la6kqD0MIUs5z5hx6AaKa+PixpPrR289+I0A==", - "dev": true - }, - "regenerate-unicode-properties": { - "version": "10.0.1", - "resolved": "https://registry.npmjs.org/regenerate-unicode-properties/-/regenerate-unicode-properties-10.0.1.tgz", - "integrity": "sha512-vn5DU6yg6h8hP/2OkQo3K7uVILvY4iu0oI4t3HFa81UPkhGJwkRwM10JEc3upjdhHjs/k8GJY1sRBhk5sr69Bw==", - "dev": true, - "requires": { - "regenerate": "^1.4.2" - } - }, - "regenerator-runtime": { - "version": "0.13.9", - "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.9.tgz", - "integrity": "sha512-p3VT+cOEgxFsRRA9X4lkI1E+k2/CtnKtU4gcxyaCUreilL/vqI6CdZ3wxVUx3UOUg+gnUOQQcRI7BmSI656MYA==", - "dev": true - }, - "regenerator-transform": { - "version": "0.14.5", - "resolved": "https://registry.npmjs.org/regenerator-transform/-/regenerator-transform-0.14.5.tgz", - "integrity": "sha512-eOf6vka5IO151Jfsw2NO9WpGX58W6wWmefK3I1zEGr0lOD0u8rwPaNqQL1aRxUaxLeKO3ArNh3VYg1KbaD+FFw==", - "dev": true, - "requires": { - "@babel/runtime": "^7.8.4" - } - }, - "regexpu-core": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-5.0.1.tgz", - "integrity": "sha512-CriEZlrKK9VJw/xQGJpQM5rY88BtuL8DM+AEwvcThHilbxiTAy8vq4iJnd2tqq8wLmjbGZzP7ZcKFjbGkmEFrw==", - "dev": true, - "requires": { - "regenerate": "^1.4.2", - "regenerate-unicode-properties": "^10.0.1", - "regjsgen": "^0.6.0", - "regjsparser": "^0.8.2", - "unicode-match-property-ecmascript": "^2.0.0", - "unicode-match-property-value-ecmascript": "^2.0.0" - } - }, - "regjsgen": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/regjsgen/-/regjsgen-0.6.0.tgz", - "integrity": "sha512-ozE883Uigtqj3bx7OhL1KNbCzGyW2NQZPl6Hs09WTvCuZD5sTI4JY58bkbQWa/Y9hxIsvJ3M8Nbf7j54IqeZbA==", - "dev": true - }, - "regjsparser": { - "version": "0.8.4", - "resolved": "https://registry.npmjs.org/regjsparser/-/regjsparser-0.8.4.tgz", - "integrity": "sha512-J3LABycON/VNEu3abOviqGHuB/LOtOQj8SKmfP9anY5GfAVw/SPjwzSjxGjbZXIxbGfqTHtJw58C2Li/WkStmA==", - "dev": true, - "requires": { - "jsesc": "~0.5.0" - }, - "dependencies": { - "jsesc": { - "version": "0.5.0", - "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-0.5.0.tgz", - "integrity": "sha1-597mbjXW/Bb3EP6R1c9p9w8IkR0=", - "dev": true - } - } - }, - "relateurl": { - "version": "0.2.7", - "resolved": "https://registry.npmjs.org/relateurl/-/relateurl-0.2.7.tgz", - "integrity": "sha1-VNvzd+UUQKypCkzSdGANP/LYiKk=", - "dev": true - }, - "renderkid": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/renderkid/-/renderkid-3.0.0.tgz", - "integrity": "sha512-q/7VIQA8lmM1hF+jn+sFSPWGlMkSAeNYcPLmDQx2zzuiDfaLrOmumR8iaUKlenFgh0XRPIUeSPlH3A+AW3Z5pg==", - "dev": true, - "requires": { - "css-select": "^4.1.3", - "dom-converter": "^0.2.0", - "htmlparser2": "^6.1.0", - "lodash": "^4.17.21", - "strip-ansi": "^6.0.1" - } - }, - "require-directory": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", - "integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I=", - "dev": true - }, - "require-from-string": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", - "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", - "dev": true - }, - "requires-port": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz", - "integrity": "sha1-kl0mAdOaxIXgkc8NpcbmlNw9yv8=", - "dev": true - }, - "resolve": { - "version": "1.22.0", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.0.tgz", - "integrity": "sha512-Hhtrw0nLeSrFQ7phPp4OOcVjLPIeMnRlr5mcnVuMe7M/7eBn98A3hmFRLoFo3DLZkivSYwhRUJTyPyWAk56WLw==", - "dev": true, - "requires": { - "is-core-module": "^2.8.1", - "path-parse": "^1.0.7", - "supports-preserve-symlinks-flag": "^1.0.0" - } - }, - "resolve-from": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", - "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", - "dev": true - }, - "restore-cursor": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-3.1.0.tgz", - "integrity": "sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==", - "dev": true, - "requires": { - "onetime": "^5.1.0", - "signal-exit": "^3.0.2" - } - }, - "retry": { - "version": "0.13.1", - "resolved": "https://registry.npmjs.org/retry/-/retry-0.13.1.tgz", - "integrity": "sha512-XQBQ3I8W1Cge0Seh+6gjj03LbmRFWuoszgK9ooCpwYIrhhoO80pfq4cUkU5DkknwfOfFteRwlZ56PYOGYyFWdg==", - "dev": true - }, - "reusify": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", - "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", - "dev": true - }, - "rimraf": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", - "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", - "dev": true, - "requires": { - "glob": "^7.1.3" - } - }, - "run-parallel": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", - "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", - "dev": true, - "requires": { - "queue-microtask": "^1.2.2" - } - }, - "safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", - "dev": true - }, - "safer-buffer": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", - "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", - "dev": true - }, - "schema-utils": { - "version": "2.7.1", - "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-2.7.1.tgz", - "integrity": "sha512-SHiNtMOUGWBQJwzISiVYKu82GiV4QYGePp3odlY1tuKO7gPtphAT5R/py0fA6xtbgLL/RvtJZnU9b8s0F1q0Xg==", - "dev": true, - "requires": { - "@types/json-schema": "^7.0.5", - "ajv": "^6.12.4", - "ajv-keywords": "^3.5.2" - } - }, - "select-hose": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/select-hose/-/select-hose-2.0.0.tgz", - "integrity": "sha1-Yl2GWPhlr0Psliv8N2o3NZpJlMo=", - "dev": true - }, - "selfsigned": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/selfsigned/-/selfsigned-2.0.1.tgz", - "integrity": "sha512-LmME957M1zOsUhG+67rAjKfiWFox3SBxE/yymatMZsAx+oMrJ0YQ8AToOnyCm7xbeg2ep37IHLxdu0o2MavQOQ==", - "dev": true, - "requires": { - "node-forge": "^1" - } - }, - "semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", - "dev": true - }, - "send": { - "version": "0.17.2", - "resolved": "https://registry.npmjs.org/send/-/send-0.17.2.tgz", - "integrity": "sha512-UJYB6wFSJE3G00nEivR5rgWp8c2xXvJ3OPWPhmuteU0IKj8nKbG3DrjiOmLwpnHGYWAVwA69zmTm++YG0Hmwww==", - "dev": true, - "requires": { - "debug": "2.6.9", - "depd": "~1.1.2", - "destroy": "~1.0.4", - "encodeurl": "~1.0.2", - "escape-html": "~1.0.3", - "etag": "~1.8.1", - "fresh": "0.5.2", - "http-errors": "1.8.1", - "mime": "1.6.0", - "ms": "2.1.3", - "on-finished": "~2.3.0", - "range-parser": "~1.2.1", - "statuses": "~1.5.0" - }, - "dependencies": { - "debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "dev": true, - "requires": { - "ms": "2.0.0" - }, - "dependencies": { - "ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", - "dev": true - } - } - }, - "ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", - "dev": true - } - } - }, - "serialize-javascript": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.0.tgz", - "integrity": "sha512-Qr3TosvguFt8ePWqsvRfrKyQXIiW+nGbYpy8XK24NQHE83caxWt+mIymTT19DGFbNWNLfEwsrkSmN64lVWB9ag==", - "dev": true, - "requires": { - "randombytes": "^2.1.0" - } - }, - "serve-index": { - "version": "1.9.1", - "resolved": "https://registry.npmjs.org/serve-index/-/serve-index-1.9.1.tgz", - "integrity": "sha1-03aNabHn2C5c4FD/9bRTvqEqkjk=", - "dev": true, - "requires": { - "accepts": "~1.3.4", - "batch": "0.6.1", - "debug": "2.6.9", - "escape-html": "~1.0.3", - "http-errors": "~1.6.2", - "mime-types": "~2.1.17", - "parseurl": "~1.3.2" - }, - "dependencies": { - "debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "dev": true, - "requires": { - "ms": "2.0.0" - } - }, - "http-errors": { - "version": "1.6.3", - "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.6.3.tgz", - "integrity": "sha1-i1VoC7S+KDoLW/TqLjhYC+HZMg0=", - "dev": true, - "requires": { - "depd": "~1.1.2", - "inherits": "2.0.3", - "setprototypeof": "1.1.0", - "statuses": ">= 1.4.0 < 2" - } - }, - "inherits": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", - "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=", - "dev": true - }, - "ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", - "dev": true - }, - "setprototypeof": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.0.tgz", - "integrity": "sha512-BvE/TwpZX4FXExxOxZyRGQQv651MSwmWKZGqvmPcRIjDqWub67kTKuIMx43cZZrS/cBBzwBcNDWoFxt2XEFIpQ==", - "dev": true - } - } - }, - "serve-static": { - "version": "1.14.2", - "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.14.2.tgz", - "integrity": "sha512-+TMNA9AFxUEGuC0z2mevogSnn9MXKb4fa7ngeRMJaaGv8vTwnIEkKi+QGvPt33HSnf8pRS+WGM0EbMtCJLKMBQ==", - "dev": true, - "requires": { - "encodeurl": "~1.0.2", - "escape-html": "~1.0.3", - "parseurl": "~1.3.3", - "send": "0.17.2" - } - }, - "setprototypeof": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", - "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==", - "dev": true - }, - "shallow-clone": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/shallow-clone/-/shallow-clone-3.0.1.tgz", - "integrity": "sha512-/6KqX+GVUdqPuPPd2LxDDxzX6CAbjJehAAOKlNpqqUpAqPM6HeL8f+o3a+JsyGjn2lv0WY8UsTgUJjU9Ok55NA==", - "dev": true, - "requires": { - "kind-of": "^6.0.2" - } - }, - "shebang-command": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz", - "integrity": "sha1-RKrGW2lbAzmJaMOfNj/uXer98eo=", - "dev": true, - "requires": { - "shebang-regex": "^1.0.0" - } - }, - "shebang-regex": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz", - "integrity": "sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM=", - "dev": true - }, - "shell-quote": { - "version": "1.7.3", - "resolved": "https://registry.npmjs.org/shell-quote/-/shell-quote-1.7.3.tgz", - "integrity": "sha512-Vpfqwm4EnqGdlsBFNmHhxhElJYrdfcxPThu+ryKS5J8L/fhAwLazFZtq+S+TWZ9ANj2piSQLGj6NQg+lKPmxrw==", - "dev": true - }, - "signal-exit": { - "version": "3.0.7", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", - "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", - "dev": true - }, - "sirv": { - "version": "1.0.19", - "resolved": "https://registry.npmjs.org/sirv/-/sirv-1.0.19.tgz", - "integrity": "sha512-JuLThK3TnZG1TAKDwNIqNq6QA2afLOCcm+iE8D1Kj3GA40pSPsxQjjJl0J8X3tsR7T+CP1GavpzLwYkgVLWrZQ==", - "dev": true, - "requires": { - "@polka/url": "^1.0.0-next.20", - "mrmime": "^1.0.0", - "totalist": "^1.0.0" - } - }, - "slash": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", - "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", - "dev": true - }, - "sockjs": { - "version": "0.3.24", - "resolved": "https://registry.npmjs.org/sockjs/-/sockjs-0.3.24.tgz", - "integrity": "sha512-GJgLTZ7vYb/JtPSSZ10hsOYIvEYsjbNU+zPdIHcUaWVNUEPivzxku31865sSSud0Da0W4lEeOPlmw93zLQchuQ==", - "dev": true, - "requires": { - "faye-websocket": "^0.11.3", - "uuid": "^8.3.2", - "websocket-driver": "^0.7.4" - } - }, - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==" - }, - "source-map-js": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.0.2.tgz", - "integrity": "sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==" - }, - "source-map-support": { - "version": "0.5.21", - "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz", - "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==", - "dev": true, - "requires": { - "buffer-from": "^1.0.0", - "source-map": "^0.6.0" - } - }, - "sourcemap-codec": { - "version": "1.4.8", - "resolved": "https://registry.npmjs.org/sourcemap-codec/-/sourcemap-codec-1.4.8.tgz", - "integrity": "sha512-9NykojV5Uih4lgo5So5dtw+f0JgJX30KCNI8gwhz2J9A15wD0Ml6tjHKwf6fTSa6fAdVBdZeNOs9eJ71qCk8vA==" - }, - "spdx-correct": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.1.1.tgz", - "integrity": "sha512-cOYcUWwhCuHCXi49RhFRCyJEK3iPj1Ziz9DpViV3tbZOwXD49QzIN3MpOLJNxh2qwq2lJJZaKMVw9qNi4jTC0w==", - "dev": true, - "requires": { - "spdx-expression-parse": "^3.0.0", - "spdx-license-ids": "^3.0.0" - } - }, - "spdx-exceptions": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.3.0.tgz", - "integrity": "sha512-/tTrYOC7PPI1nUAgx34hUpqXuyJG+DTHJTnIULG4rDygi4xu/tfgmq1e1cIRwRzwZgo4NLySi+ricLkZkw4i5A==", - "dev": true - }, - "spdx-expression-parse": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.1.tgz", - "integrity": "sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==", - "dev": true, - "requires": { - "spdx-exceptions": "^2.1.0", - "spdx-license-ids": "^3.0.0" - } - }, - "spdx-license-ids": { - "version": "3.0.11", - "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.11.tgz", - "integrity": "sha512-Ctl2BrFiM0X3MANYgj3CkygxhRmr9mi6xhejbdO960nF6EDJApTYpn0BQnDKlnNBULKiCN1n3w9EBkHK8ZWg+g==", - "dev": true - }, - "spdy": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/spdy/-/spdy-4.0.2.tgz", - "integrity": "sha512-r46gZQZQV+Kl9oItvl1JZZqJKGr+oEkB08A6BzkiR7593/7IbtuncXHd2YoYeTsG4157ZssMu9KYvUHLcjcDoA==", - "dev": true, - "requires": { - "debug": "^4.1.0", - "handle-thing": "^2.0.0", - "http-deceiver": "^1.2.7", - "select-hose": "^2.0.0", - "spdy-transport": "^3.0.0" - } - }, - "spdy-transport": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/spdy-transport/-/spdy-transport-3.0.0.tgz", - "integrity": "sha512-hsLVFE5SjA6TCisWeJXFKniGGOpBgMLmerfO2aCyCU5s7nJ/rpAepqmFifv/GCbSbueEeAJJnmSQ2rKC/g8Fcw==", - "dev": true, - "requires": { - "debug": "^4.1.0", - "detect-node": "^2.0.4", - "hpack.js": "^2.1.6", - "obuf": "^1.1.2", - "readable-stream": "^3.0.6", - "wbuf": "^1.7.3" - } - }, - "ssri": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/ssri/-/ssri-8.0.1.tgz", - "integrity": "sha512-97qShzy1AiyxvPNIkLWoGua7xoQzzPjQ0HAH4B0rWKo7SZ6USuPcrUiAFrws0UH8RrbWmgq3LMTObhPIHbbBeQ==", - "dev": true, - "requires": { - "minipass": "^3.1.1" - } - }, - "stable": { - "version": "0.1.8", - "resolved": "https://registry.npmjs.org/stable/-/stable-0.1.8.tgz", - "integrity": "sha512-ji9qxRnOVfcuLDySj9qzhGSEFVobyt1kIOSkj1qZzYLzq7Tos/oUUWvotUPQLlrsidqsK6tBH89Bc9kL5zHA6w==", - "dev": true - }, - "stackframe": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/stackframe/-/stackframe-1.2.1.tgz", - "integrity": "sha512-h88QkzREN/hy8eRdyNhhsO7RSJ5oyTqxxmmn0dzBIMUclZsjpfmrsg81vp8mjjAs2vAZ72nyWxRUwSwmh0e4xg==", - "dev": true - }, - "statuses": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz", - "integrity": "sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow=", - "dev": true - }, - "string-width": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "dev": true, - "requires": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" - } - }, - "string_decoder": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", - "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", - "dev": true, - "requires": { - "safe-buffer": "~5.2.0" - }, - "dependencies": { - "safe-buffer": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", - "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", - "dev": true - } - } - }, - "strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "dev": true, - "requires": { - "ansi-regex": "^5.0.1" - } - }, - "strip-eof": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/strip-eof/-/strip-eof-1.0.0.tgz", - "integrity": "sha1-u0P/VZim6wXYm1n80SnJgzE2Br8=", - "dev": true - }, - "strip-final-newline": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz", - "integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==", - "dev": true - }, - "stylehacks": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/stylehacks/-/stylehacks-5.1.0.tgz", - "integrity": "sha512-SzLmvHQTrIWfSgljkQCw2++C9+Ne91d/6Sp92I8c5uHTcy/PgeHamwITIbBW9wnFTY/3ZfSXR9HIL6Ikqmcu6Q==", - "dev": true, - "requires": { - "browserslist": "^4.16.6", - "postcss-selector-parser": "^6.0.4" - } - }, - "supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", - "dev": true, - "requires": { - "has-flag": "^3.0.0" - } - }, - "supports-preserve-symlinks-flag": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", - "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", - "dev": true - }, - "svg-tags": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/svg-tags/-/svg-tags-1.0.0.tgz", - "integrity": "sha1-WPcc7jvVGbWdSyqEO2x95krAR2Q=", - "dev": true - }, - "svgo": { - "version": "2.8.0", - "resolved": "https://registry.npmjs.org/svgo/-/svgo-2.8.0.tgz", - "integrity": "sha512-+N/Q9kV1+F+UeWYoSiULYo4xYSDQlTgb+ayMobAXPwMnLvop7oxKMo9OzIrX5x3eS4L4f2UHhc9axXwY8DpChg==", - "dev": true, - "requires": { - "@trysound/sax": "0.2.0", - "commander": "^7.2.0", - "css-select": "^4.1.3", - "css-tree": "^1.1.3", - "csso": "^4.2.0", - "picocolors": "^1.0.0", - "stable": "^0.1.8" - }, - "dependencies": { - "commander": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/commander/-/commander-7.2.0.tgz", - "integrity": "sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==", - "dev": true - } - } - }, - "tapable": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.2.1.tgz", - "integrity": "sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ==", - "dev": true - }, - "terser": { - "version": "5.12.1", - "resolved": "https://registry.npmjs.org/terser/-/terser-5.12.1.tgz", - "integrity": "sha512-NXbs+7nisos5E+yXwAD+y7zrcTkMqb0dEJxIGtSKPdCBzopf7ni4odPul2aechpV7EXNvOudYOX2bb5tln1jbQ==", - "dev": true, - "requires": { - "acorn": "^8.5.0", - "commander": "^2.20.0", - "source-map": "~0.7.2", - "source-map-support": "~0.5.20" - }, - "dependencies": { - "source-map": { - "version": "0.7.3", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.3.tgz", - "integrity": "sha512-CkCj6giN3S+n9qrYiBTX5gystlENnRW5jZeNLHpe6aue+SrHcG5VYwujhW9s4dY31mEGsxBDrHR6oI69fTXsaQ==", - "dev": true - } - } - }, - "terser-webpack-plugin": { - "version": "5.3.1", - "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-5.3.1.tgz", - "integrity": "sha512-GvlZdT6wPQKbDNW/GDQzZFg/j4vKU96yl2q6mcUkzKOgW4gwf1Z8cZToUCrz31XHlPWH8MVb1r2tFtdDtTGJ7g==", - "dev": true, - "requires": { - "jest-worker": "^27.4.5", - "schema-utils": "^3.1.1", - "serialize-javascript": "^6.0.0", - "source-map": "^0.6.1", - "terser": "^5.7.2" - }, - "dependencies": { - "schema-utils": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.1.1.tgz", - "integrity": "sha512-Y5PQxS4ITlC+EahLuXaY86TXfR7Dc5lw294alXOq86JAHCihAIZfqv8nNCWvaEJvaC51uN9hbLGeV0cFBdH+Fw==", - "dev": true, - "requires": { - "@types/json-schema": "^7.0.8", - "ajv": "^6.12.5", - "ajv-keywords": "^3.5.2" - } - } - } - }, - "thenify": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/thenify/-/thenify-3.3.1.tgz", - "integrity": "sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw==", - "dev": true, - "requires": { - "any-promise": "^1.0.0" - } - }, - "thenify-all": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/thenify-all/-/thenify-all-1.6.0.tgz", - "integrity": "sha1-GhkY1ALY/D+Y+/I02wvMjMEOlyY=", - "dev": true, - "requires": { - "thenify": ">= 3.1.0 < 4" - } - }, - "thread-loader": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/thread-loader/-/thread-loader-3.0.4.tgz", - "integrity": "sha512-ByaL2TPb+m6yArpqQUZvP+5S1mZtXsEP7nWKKlAUTm7fCml8kB5s1uI3+eHRP2bk5mVYfRSBI7FFf+tWEyLZwA==", - "dev": true, - "requires": { - "json-parse-better-errors": "^1.0.2", - "loader-runner": "^4.1.0", - "loader-utils": "^2.0.0", - "neo-async": "^2.6.2", - "schema-utils": "^3.0.0" - }, - "dependencies": { - "schema-utils": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.1.1.tgz", - "integrity": "sha512-Y5PQxS4ITlC+EahLuXaY86TXfR7Dc5lw294alXOq86JAHCihAIZfqv8nNCWvaEJvaC51uN9hbLGeV0cFBdH+Fw==", - "dev": true, - "requires": { - "@types/json-schema": "^7.0.8", - "ajv": "^6.12.5", - "ajv-keywords": "^3.5.2" - } - } - } - }, - "thunky": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/thunky/-/thunky-1.1.0.tgz", - "integrity": "sha512-eHY7nBftgThBqOyHGVN+l8gF0BucP09fMo0oO/Lb0w1OF80dJv+lDVpXG60WMQvkcxAkNybKsrEIE3ZtKGmPrA==", - "dev": true - }, - "to-fast-properties": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", - "integrity": "sha1-3F5pjL0HkmW8c+A3doGk5Og/YW4=", - "dev": true - }, - "to-regex-range": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", - "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", - "dev": true, - "requires": { - "is-number": "^7.0.0" - } - }, - "toidentifier": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", - "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==", - "dev": true - }, - "totalist": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/totalist/-/totalist-1.1.0.tgz", - "integrity": "sha512-gduQwd1rOdDMGxFG1gEvhV88Oirdo2p+KjoYFU7k2g+i7n6AFFbDQ5kMPUsW0pNbfQsB/cwXvT1i4Bue0s9g5g==", - "dev": true - }, - "tr46": { - "version": "0.0.3", - "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", - "integrity": "sha1-gYT9NH2snNwYWZLzpmIuFLnZq2o=", - "dev": true - }, - "tslib": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.1.tgz", - "integrity": "sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw==", - "dev": true - }, - "type-fest": { - "version": "2.12.1", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-2.12.1.tgz", - "integrity": "sha512-AiknQSEqKVGDDjtZqeKrUoTlcj7FKhupmnVUgz6KoOKtvMwRGE6hUNJ/nVear+h7fnUPO1q/htSkYKb1pyntkQ==" - }, - "type-is": { - "version": "1.6.18", - "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", - "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", - "dev": true, - "requires": { - "media-typer": "0.3.0", - "mime-types": "~2.1.24" - } - }, - "unicode-canonical-property-names-ecmascript": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-2.0.0.tgz", - "integrity": "sha512-yY5PpDlfVIU5+y/BSCxAJRBIS1Zc2dDG3Ujq+sR0U+JjUevW2JhocOF+soROYDSaAezOzOKuyyixhD6mBknSmQ==", - "dev": true - }, - "unicode-match-property-ecmascript": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/unicode-match-property-ecmascript/-/unicode-match-property-ecmascript-2.0.0.tgz", - "integrity": "sha512-5kaZCrbp5mmbz5ulBkDkbY0SsPOjKqVS35VpL9ulMPfSl0J0Xsm+9Evphv9CoIZFwre7aJoa94AY6seMKGVN5Q==", - "dev": true, - "requires": { - "unicode-canonical-property-names-ecmascript": "^2.0.0", - "unicode-property-aliases-ecmascript": "^2.0.0" - } - }, - "unicode-match-property-value-ecmascript": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/unicode-match-property-value-ecmascript/-/unicode-match-property-value-ecmascript-2.0.0.tgz", - "integrity": "sha512-7Yhkc0Ye+t4PNYzOGKedDhXbYIBe1XEQYQxOPyhcXNMJ0WCABqqj6ckydd6pWRZTHV4GuCPKdBAUiMc60tsKVw==", - "dev": true - }, - "unicode-property-aliases-ecmascript": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/unicode-property-aliases-ecmascript/-/unicode-property-aliases-ecmascript-2.0.0.tgz", - "integrity": "sha512-5Zfuy9q/DFr4tfO7ZPeVXb1aPoeQSdeFMLpYuFebehDAhbuevLs5yxSZmIFN1tP5F9Wl4IpJrYojg85/zgyZHQ==", - "dev": true - }, - "universalify": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.0.tgz", - "integrity": "sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ==", - "dev": true - }, - "unpipe": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", - "integrity": "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw=", - "dev": true - }, - "uri-js": { - "version": "4.4.1", - "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", - "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", - "dev": true, - "requires": { - "punycode": "^2.1.0" - } - }, - "util-deprecate": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", - "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=", - "dev": true - }, - "utila": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/utila/-/utila-0.4.0.tgz", - "integrity": "sha1-ihagXURWV6Oupe7MWxKk+lN5dyw=", - "dev": true - }, - "utils-merge": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", - "integrity": "sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM=", - "dev": true - }, - "uuid": { - "version": "8.3.2", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", - "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", - "dev": true - }, - "validate-npm-package-license": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz", - "integrity": "sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==", - "dev": true, - "requires": { - "spdx-correct": "^3.0.0", - "spdx-expression-parse": "^3.0.0" - } - }, - "vary": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", - "integrity": "sha1-IpnwLG3tMNSllhsLn3RSShj2NPw=", - "dev": true - }, - "vue": { - "version": "3.2.31", - "resolved": "https://registry.npmjs.org/vue/-/vue-3.2.31.tgz", - "integrity": "sha512-odT3W2tcffTiQCy57nOT93INw1auq5lYLLYtWpPYQQYQOOdHiqFct9Xhna6GJ+pJQaF67yZABraH47oywkJgFw==", - "requires": { - "@vue/compiler-dom": "3.2.31", - "@vue/compiler-sfc": "3.2.31", - "@vue/runtime-dom": "3.2.31", - "@vue/server-renderer": "3.2.31", - "@vue/shared": "3.2.31" - } - }, - "vue-hot-reload-api": { - "version": "2.3.4", - "resolved": "https://registry.npmjs.org/vue-hot-reload-api/-/vue-hot-reload-api-2.3.4.tgz", - "integrity": "sha512-BXq3jwIagosjgNVae6tkHzzIk6a8MHFtzAdwhnV5VlvPTFxDCvIttgSiHWjdGoTJvXtmRu5HacExfdarRcFhog==", - "dev": true - }, - "vue-loader": { - "version": "17.0.0", - "resolved": "https://registry.npmjs.org/vue-loader/-/vue-loader-17.0.0.tgz", - "integrity": "sha512-OWSXjrzIvbF2LtOUmxT3HYgwwubbfFelN8PAP9R9dwpIkj48TVioHhWWSx7W7fk+iF5cgg3CBJRxwTdtLU4Ecg==", - "dev": true, - "requires": { - "chalk": "^4.1.0", - "hash-sum": "^2.0.0", - "loader-utils": "^2.0.0" - }, - "dependencies": { - "ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "requires": { - "color-convert": "^2.0.1" - } - }, - "chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - } - }, - "color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "requires": { - "color-name": "~1.1.4" - } - }, - "color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true - }, - "supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "requires": { - "has-flag": "^4.0.0" - } - } - } - }, - "vue-style-loader": { - "version": "4.1.3", - "resolved": "https://registry.npmjs.org/vue-style-loader/-/vue-style-loader-4.1.3.tgz", - "integrity": "sha512-sFuh0xfbtpRlKfm39ss/ikqs9AbKCoXZBpHeVZ8Tx650o0k0q/YCM7FRvigtxpACezfq6af+a7JeqVTWvncqDg==", - "dev": true, - "requires": { - "hash-sum": "^1.0.2", - "loader-utils": "^1.0.2" - }, - "dependencies": { - "hash-sum": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/hash-sum/-/hash-sum-1.0.2.tgz", - "integrity": "sha1-M7QHd3VMZDJXPBIMw4CLvRDUfwQ=", - "dev": true - }, - "json5": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.1.tgz", - "integrity": "sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow==", - "dev": true, - "requires": { - "minimist": "^1.2.0" - } - }, - "loader-utils": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.4.0.tgz", - "integrity": "sha512-qH0WSMBtn/oHuwjy/NucEgbx5dbxxnxup9s4PVXJUDHZBQY+s0NWA9rJf53RBnQZxfch7euUui7hpoAPvALZdA==", - "dev": true, - "requires": { - "big.js": "^5.2.2", - "emojis-list": "^3.0.0", - "json5": "^1.0.1" - } - } - } - }, - "vue-template-es2015-compiler": { - "version": "1.9.1", - "resolved": "https://registry.npmjs.org/vue-template-es2015-compiler/-/vue-template-es2015-compiler-1.9.1.tgz", - "integrity": "sha512-4gDntzrifFnCEvyoO8PqyJDmguXgVPxKiIxrBKjIowvL9l+N66196+72XVYR8BBf1Uv1Fgt3bGevJ+sEmxfZzw==", - "dev": true - }, - "vuex": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/vuex/-/vuex-4.0.2.tgz", - "integrity": "sha512-M6r8uxELjZIK8kTKDGgZTYX/ahzblnzC4isU1tpmEuOIIKmV+TRdc+H4s8ds2NuZ7wpUTdGRzJRtoj+lI+pc0Q==", - "requires": { - "@vue/devtools-api": "^6.0.0-beta.11" - } - }, - "watchpack": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.3.1.tgz", - "integrity": "sha512-x0t0JuydIo8qCNctdDrn1OzH/qDzk2+rdCOC3YzumZ42fiMqmQ7T3xQurykYMhYfHaPHTp4ZxAx2NfUo1K6QaA==", - "dev": true, - "requires": { - "glob-to-regexp": "^0.4.1", - "graceful-fs": "^4.1.2" - } - }, - "wbuf": { - "version": "1.7.3", - "resolved": "https://registry.npmjs.org/wbuf/-/wbuf-1.7.3.tgz", - "integrity": "sha512-O84QOnr0icsbFGLS0O3bI5FswxzRr8/gHwWkDlQFskhSPryQXvrTMxjxGP4+iWYoauLoBvfDpkrOauZ+0iZpDA==", - "dev": true, - "requires": { - "minimalistic-assert": "^1.0.0" - } - }, - "wcwidth": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/wcwidth/-/wcwidth-1.0.1.tgz", - "integrity": "sha1-8LDc+RW8X/FSivrbLA4XtTLaL+g=", - "dev": true, - "requires": { - "defaults": "^1.0.3" - } - }, - "webidl-conversions": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", - "integrity": "sha1-JFNCdeKnvGvnvIZhHMFq4KVlSHE=", - "dev": true - }, - "webpack": { - "version": "5.71.0", - "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.71.0.tgz", - "integrity": "sha512-g4dFT7CFG8LY0iU5G8nBL6VlkT21Z7dcYDpJAEJV5Q1WLb9UwnFbrem1k7K52ILqEmomN7pnzWFxxE6SlDY56A==", - "dev": true, - "requires": { - "@types/eslint-scope": "^3.7.3", - "@types/estree": "^0.0.51", - "@webassemblyjs/ast": "1.11.1", - "@webassemblyjs/wasm-edit": "1.11.1", - "@webassemblyjs/wasm-parser": "1.11.1", - "acorn": "^8.4.1", - "acorn-import-assertions": "^1.7.6", - "browserslist": "^4.14.5", - "chrome-trace-event": "^1.0.2", - "enhanced-resolve": "^5.9.2", - "es-module-lexer": "^0.9.0", - "eslint-scope": "5.1.1", - "events": "^3.2.0", - "glob-to-regexp": "^0.4.1", - "graceful-fs": "^4.2.9", - "json-parse-better-errors": "^1.0.2", - "loader-runner": "^4.2.0", - "mime-types": "^2.1.27", - "neo-async": "^2.6.2", - "schema-utils": "^3.1.0", - "tapable": "^2.1.1", - "terser-webpack-plugin": "^5.1.3", - "watchpack": "^2.3.1", - "webpack-sources": "^3.2.3" - }, - "dependencies": { - "schema-utils": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.1.1.tgz", - "integrity": "sha512-Y5PQxS4ITlC+EahLuXaY86TXfR7Dc5lw294alXOq86JAHCihAIZfqv8nNCWvaEJvaC51uN9hbLGeV0cFBdH+Fw==", - "dev": true, - "requires": { - "@types/json-schema": "^7.0.8", - "ajv": "^6.12.5", - "ajv-keywords": "^3.5.2" - } - } - } - }, - "webpack-bundle-analyzer": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/webpack-bundle-analyzer/-/webpack-bundle-analyzer-4.5.0.tgz", - "integrity": "sha512-GUMZlM3SKwS8Z+CKeIFx7CVoHn3dXFcUAjT/dcZQQmfSZGvitPfMob2ipjai7ovFFqPvTqkEZ/leL4O0YOdAYQ==", - "dev": true, - "requires": { - "acorn": "^8.0.4", - "acorn-walk": "^8.0.0", - "chalk": "^4.1.0", - "commander": "^7.2.0", - "gzip-size": "^6.0.0", - "lodash": "^4.17.20", - "opener": "^1.5.2", - "sirv": "^1.0.7", - "ws": "^7.3.1" - }, - "dependencies": { - "ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "requires": { - "color-convert": "^2.0.1" - } - }, - "chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - } - }, - "color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "requires": { - "color-name": "~1.1.4" - } - }, - "color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "commander": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/commander/-/commander-7.2.0.tgz", - "integrity": "sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==", - "dev": true - }, - "has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true - }, - "supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "requires": { - "has-flag": "^4.0.0" - } - } - } - }, - "webpack-chain": { - "version": "6.5.1", - "resolved": "https://registry.npmjs.org/webpack-chain/-/webpack-chain-6.5.1.tgz", - "integrity": "sha512-7doO/SRtLu8q5WM0s7vPKPWX580qhi0/yBHkOxNkv50f6qB76Zy9o2wRTrrPULqYTvQlVHuvbA8v+G5ayuUDsA==", - "dev": true, - "requires": { - "deepmerge": "^1.5.2", - "javascript-stringify": "^2.0.1" - } - }, - "webpack-dev-middleware": { - "version": "5.3.1", - "resolved": "https://registry.npmjs.org/webpack-dev-middleware/-/webpack-dev-middleware-5.3.1.tgz", - "integrity": "sha512-81EujCKkyles2wphtdrnPg/QqegC/AtqNH//mQkBYSMqwFVCQrxM6ktB2O/SPlZy7LqeEfTbV3cZARGQz6umhg==", - "dev": true, - "requires": { - "colorette": "^2.0.10", - "memfs": "^3.4.1", - "mime-types": "^2.1.31", - "range-parser": "^1.2.1", - "schema-utils": "^4.0.0" - }, - "dependencies": { - "ajv": { - "version": "8.11.0", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.11.0.tgz", - "integrity": "sha512-wGgprdCvMalC0BztXvitD2hC04YffAvtsUn93JbGXYLAtCUO4xd17mCCZQxUOItiBwZvJScWo8NIvQMQ71rdpg==", - "dev": true, - "requires": { - "fast-deep-equal": "^3.1.1", - "json-schema-traverse": "^1.0.0", - "require-from-string": "^2.0.2", - "uri-js": "^4.2.2" - } - }, - "ajv-keywords": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-5.1.0.tgz", - "integrity": "sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw==", - "dev": true, - "requires": { - "fast-deep-equal": "^3.1.3" - } - }, - "json-schema-traverse": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", - "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", - "dev": true - }, - "schema-utils": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.0.0.tgz", - "integrity": "sha512-1edyXKgh6XnJsJSQ8mKWXnN/BVaIbFMLpouRUrXgVq7WYne5kw3MW7UPhO44uRXQSIpTSXoJbmrR2X0w9kUTyg==", - "dev": true, - "requires": { - "@types/json-schema": "^7.0.9", - "ajv": "^8.8.0", - "ajv-formats": "^2.1.1", - "ajv-keywords": "^5.0.0" - } - } - } - }, - "webpack-dev-server": { - "version": "4.8.1", - "resolved": "https://registry.npmjs.org/webpack-dev-server/-/webpack-dev-server-4.8.1.tgz", - "integrity": "sha512-dwld70gkgNJa33czmcj/PlKY/nOy/BimbrgZRaR9vDATBQAYgLzggR0nxDtPLJiLrMgZwbE6RRfJ5vnBBasTyg==", - "dev": true, - "requires": { - "@types/bonjour": "^3.5.9", - "@types/connect-history-api-fallback": "^1.3.5", - "@types/express": "^4.17.13", - "@types/serve-index": "^1.9.1", - "@types/sockjs": "^0.3.33", - "@types/ws": "^8.5.1", - "ansi-html-community": "^0.0.8", - "bonjour-service": "^1.0.11", - "chokidar": "^3.5.3", - "colorette": "^2.0.10", - "compression": "^1.7.4", - "connect-history-api-fallback": "^1.6.0", - "default-gateway": "^6.0.3", - "express": "^4.17.3", - "graceful-fs": "^4.2.6", - "html-entities": "^2.3.2", - "http-proxy-middleware": "^2.0.3", - "ipaddr.js": "^2.0.1", - "open": "^8.0.9", - "p-retry": "^4.5.0", - "portfinder": "^1.0.28", - "rimraf": "^3.0.2", - "schema-utils": "^4.0.0", - "selfsigned": "^2.0.1", - "serve-index": "^1.9.1", - "sockjs": "^0.3.21", - "spdy": "^4.0.2", - "webpack-dev-middleware": "^5.3.1", - "ws": "^8.4.2" - }, - "dependencies": { - "ajv": { - "version": "8.11.0", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.11.0.tgz", - "integrity": "sha512-wGgprdCvMalC0BztXvitD2hC04YffAvtsUn93JbGXYLAtCUO4xd17mCCZQxUOItiBwZvJScWo8NIvQMQ71rdpg==", - "dev": true, - "requires": { - "fast-deep-equal": "^3.1.1", - "json-schema-traverse": "^1.0.0", - "require-from-string": "^2.0.2", - "uri-js": "^4.2.2" - } - }, - "ajv-keywords": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-5.1.0.tgz", - "integrity": "sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw==", - "dev": true, - "requires": { - "fast-deep-equal": "^3.1.3" - } - }, - "json-schema-traverse": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", - "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", - "dev": true - }, - "schema-utils": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.0.0.tgz", - "integrity": "sha512-1edyXKgh6XnJsJSQ8mKWXnN/BVaIbFMLpouRUrXgVq7WYne5kw3MW7UPhO44uRXQSIpTSXoJbmrR2X0w9kUTyg==", - "dev": true, - "requires": { - "@types/json-schema": "^7.0.9", - "ajv": "^8.8.0", - "ajv-formats": "^2.1.1", - "ajv-keywords": "^5.0.0" - } - }, - "ws": { - "version": "8.5.0", - "resolved": "https://registry.npmjs.org/ws/-/ws-8.5.0.tgz", - "integrity": "sha512-BWX0SWVgLPzYwF8lTzEy1egjhS4S4OEAHfsO8o65WOVsrnSRGaSiUaa9e0ggGlkMTtBlmOpEXiie9RUcBO86qg==", - "dev": true - } - } - }, - "webpack-merge": { - "version": "5.8.0", - "resolved": "https://registry.npmjs.org/webpack-merge/-/webpack-merge-5.8.0.tgz", - "integrity": "sha512-/SaI7xY0831XwP6kzuwhKWVKDP9t1QY1h65lAFLbZqMPIuYcD9QAW4u9STIbU9kaJbPBB/geU/gLr1wDjOhQ+Q==", - "dev": true, - "requires": { - "clone-deep": "^4.0.1", - "wildcard": "^2.0.0" - } - }, - "webpack-sources": { - "version": "3.2.3", - "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-3.2.3.tgz", - "integrity": "sha512-/DyMEOrDgLKKIG0fmvtz+4dUX/3Ghozwgm6iPp8KRhvn+eQf9+Q7GWxVNMk3+uCPWfdXYC4ExGBckIXdFEfH1w==", - "dev": true - }, - "webpack-virtual-modules": { - "version": "0.4.3", - "resolved": "https://registry.npmjs.org/webpack-virtual-modules/-/webpack-virtual-modules-0.4.3.tgz", - "integrity": "sha512-5NUqC2JquIL2pBAAo/VfBP6KuGkHIZQXW/lNKupLPfhViwh8wNsu0BObtl09yuKZszeEUfbXz8xhrHvSG16Nqw==", - "dev": true - }, - "websocket-driver": { - "version": "0.7.4", - "resolved": "https://registry.npmjs.org/websocket-driver/-/websocket-driver-0.7.4.tgz", - "integrity": "sha512-b17KeDIQVjvb0ssuSDF2cYXSg2iztliJ4B9WdsuB6J952qCPKmnVq4DyW5motImXHDC1cBT/1UezrJVsKw5zjg==", - "dev": true, - "requires": { - "http-parser-js": ">=0.5.1", - "safe-buffer": ">=5.1.0", - "websocket-extensions": ">=0.1.1" - } - }, - "websocket-extensions": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/websocket-extensions/-/websocket-extensions-0.1.4.tgz", - "integrity": "sha512-OqedPIGOfsDlo31UNwYbCFMSaO9m9G/0faIHj5/dZFDMFqPTcx6UwqyOy3COEaEOg/9VsGIpdqn62W5KhoKSpg==", - "dev": true - }, - "whatwg-fetch": { - "version": "3.6.2", - "resolved": "https://registry.npmjs.org/whatwg-fetch/-/whatwg-fetch-3.6.2.tgz", - "integrity": "sha512-bJlen0FcuU/0EMLrdbJ7zOnW6ITZLrZMIarMUVmdKtsGvZna8vxKYaexICWPfZ8qwf9fzNq+UEIZrnSaApt6RA==", - "dev": true - }, - "whatwg-url": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", - "integrity": "sha1-lmRU6HZUYuN2RNNib2dCzotwll0=", - "dev": true, - "requires": { - "tr46": "~0.0.3", - "webidl-conversions": "^3.0.0" - } - }, - "which": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", - "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", - "dev": true, - "requires": { - "isexe": "^2.0.0" - } - }, - "wildcard": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/wildcard/-/wildcard-2.0.0.tgz", - "integrity": "sha512-JcKqAHLPxcdb9KM49dufGXn2x3ssnfjbcaQdLlfZsL9rH9wgDQjUtDxbo8NE0F6SFvydeu1VhZe7hZuHsB2/pw==", - "dev": true - }, - "wrap-ansi": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", - "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", - "dev": true, - "requires": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" - }, - "dependencies": { - "ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "requires": { - "color-convert": "^2.0.1" - } - }, - "color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "requires": { - "color-name": "~1.1.4" - } - }, - "color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - } - } - }, - "wrappy": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", - "dev": true - }, - "ws": { - "version": "7.5.7", - "resolved": "https://registry.npmjs.org/ws/-/ws-7.5.7.tgz", - "integrity": "sha512-KMvVuFzpKBuiIXW3E4u3mySRO2/mCHSyZDJQM5NQ9Q9KHWHWh0NHgfbRMLLrceUK5qAL4ytALJbpRMjixFZh8A==", - "dev": true - }, - "y18n": { - "version": "5.0.8", - "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", - "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", - "dev": true - }, - "yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", - "dev": true - }, - "yaml": { - "version": "1.10.2", - "resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.2.tgz", - "integrity": "sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==", - "dev": true - }, - "yargs": { - "version": "16.2.0", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", - "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==", - "dev": true, - "requires": { - "cliui": "^7.0.2", - "escalade": "^3.1.1", - "get-caller-file": "^2.0.5", - "require-directory": "^2.1.1", - "string-width": "^4.2.0", - "y18n": "^5.0.5", - "yargs-parser": "^20.2.2" - } - }, - "yargs-parser": { - "version": "20.2.9", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.9.tgz", - "integrity": "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==", - "dev": true - } - } -} diff --git a/applications/launchpad/gui-vue/package.json b/applications/launchpad/gui-vue/package.json deleted file mode 100644 index 2f361836c6..0000000000 --- a/applications/launchpad/gui-vue/package.json +++ /dev/null @@ -1,48 +0,0 @@ -{ - "name": "gui-vue", - "version": "0.1.0", - "private": true, - "scripts": { - "serve": "vue-cli-service serve", - "build": "vue-cli-service build", - "lint": "vue-cli-service lint", - "tauri": "tauri", - "postbuild": "touch ./dist/.gitkeep || type NUL > ./dist/.gitkeep" - }, - "dependencies": { - "@fortawesome/fontawesome-svg-core": "^1.2.36", - "@fortawesome/free-solid-svg-icons": "^5.15.4", - "@fortawesome/vue-fontawesome": "^3.0.0-5", - "@oruga-ui/oruga-next": "^0.5.5", - "@tauri-apps/api": "^1.0.0-rc.3", - "CBuffer": "^2.2.0", - "vue": "^3.2.31", - "vuex": "^4.0.2" - }, - "devDependencies": { - "@tauri-apps/cli": "^1.0.0-beta.10", - "@vue/cli-plugin-babel": "^5.0.4", - "@vue/cli-service": "^5.0.4", - "@vue/compiler-sfc": "^3.0.0", - "babel-eslint": "^10.1.0" - }, - "eslintConfig": { - "root": true, - "env": { - "node": true - }, - "extends": [ - "plugin:vue/vue3-essential", - "eslint:recommended" - ], - "parserOptions": { - "parser": "babel-eslint" - }, - "rules": {} - }, - "browserslist": [ - "> 1%", - "last 2 versions", - "not dead" - ] -} diff --git a/applications/launchpad/gui-vue/public/favicon.ico b/applications/launchpad/gui-vue/public/favicon.ico deleted file mode 100644 index df36fcfb72..0000000000 Binary files a/applications/launchpad/gui-vue/public/favicon.ico and /dev/null differ diff --git a/applications/launchpad/gui-vue/public/index.html b/applications/launchpad/gui-vue/public/index.html deleted file mode 100644 index 3e5a139621..0000000000 --- a/applications/launchpad/gui-vue/public/index.html +++ /dev/null @@ -1,17 +0,0 @@ - - - - - - - - <%= htmlWebpackPlugin.options.title %> - - - -
- - - diff --git a/applications/launchpad/gui-vue/src/App.vue b/applications/launchpad/gui-vue/src/App.vue deleted file mode 100644 index 526ec3de5c..0000000000 --- a/applications/launchpad/gui-vue/src/App.vue +++ /dev/null @@ -1,44 +0,0 @@ - - - - - diff --git a/applications/launchpad/gui-vue/src/assets/logo.png b/applications/launchpad/gui-vue/src/assets/logo.png deleted file mode 100644 index f3d2503fc2..0000000000 Binary files a/applications/launchpad/gui-vue/src/assets/logo.png and /dev/null differ diff --git a/applications/launchpad/gui-vue/src/assets/material.css b/applications/launchpad/gui-vue/src/assets/material.css deleted file mode 100644 index df6736e364..0000000000 --- a/applications/launchpad/gui-vue/src/assets/material.css +++ /dev/null @@ -1,12183 +0,0 @@ -/* Copyright 2021. The Tari Project */ -/* */ -/* Redistribution and use in source and binary forms, with or without modification, are permitted provided that the */ -/* following conditions are met: */ -/* */ -/* 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following */ -/* disclaimer. */ -/* */ -/* 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the */ -/* following disclaimer in the documentation and/or other materials provided with the distribution. */ -/* */ -/* 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote */ -/* products derived from this software without specific prior written permission. */ -/* */ -/* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, */ -/* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE */ -/* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, */ -/* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR */ -/* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, */ -/* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE */ -/* USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -/* */ - -.card { - font-size: .875rem; - font-weight: 400 -} - -/*! - * Bootstrap v4.0.0 (https://getbootstrap.com) - * Copyright 2011-2018 The Bootstrap Authors - * Copyright 2011-2018 Twitter, Inc. - * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) - */ -:root { - --blue: #2196f3; - --indigo: #3f51b5; - --purple: #9c27b0; - --pink: #e91e63; - --red: #f44336; - --orange: #ff9800; - --yellow: #ffeb3b; - --green: #4caf50; - --teal: #009688; - --cyan: #00bcd4; - --white: #fff; - --gray: #6c757d; - --gray-dark: #343a40; - --primary: #009688; - --secondary: #6c757d; - --success: #4caf50; - --info: #03a9f4; - --warning: #ff5722; - --danger: #f44336; - --light: #f5f5f5; - --dark: #424242; - --breakpoint-xs: 0; - --breakpoint-sm: 576px; - --breakpoint-md: 768px; - --breakpoint-lg: 992px; - --breakpoint-xl: 1200px; - --font-family-sans-serif: "Roboto", "Helvetica", "Arial", sans-serif; - --font-family-monospace: SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace -} - -*, :after, :before { - box-sizing: border-box -} - -html { - font-family: sans-serif; - line-height: 1.15; - -webkit-text-size-adjust: 100%; - -ms-text-size-adjust: 100%; - -ms-overflow-style: scrollbar; - -webkit-tap-highlight-color: transparent -} - -@-ms-viewport { - width: device-width -} - -article, aside, dialog, figcaption, figure, footer, header, hgroup, main, nav, section { - display: block -} - -body { - margin: 0; - font-family: Roboto, Helvetica, Arial, sans-serif; - font-size: 1rem; - line-height: 1.5; - color: #212529; - text-align: left; - background-color: #fafafa -} - -[tabindex="-1"]:focus { - outline: 0 !important -} - -hr { - box-sizing: content-box; - height: 0; - overflow: visible -} - -h1, h2, h3, h4, h5, h6 { - margin-top: 0; - margin-bottom: .5rem -} - -p { - margin-top: 0; - margin-bottom: 1rem -} - -abbr[data-original-title], abbr[title] { - text-decoration: underline; - text-decoration: underline dotted; - cursor: help; - border-bottom: 0 -} - -address { - font-style: normal; - line-height: inherit -} - -address, dl, ol, ul { - margin-bottom: 1rem -} - -dl, ol, ul { - margin-top: 0 -} - -ol ol, ol ul, ul ol, ul ul { - margin-bottom: 0 -} - -dt { - font-weight: 700 -} - -dd { - margin-bottom: .5rem; - margin-left: 0 -} - -blockquote { - margin: 0 0 1rem -} - -dfn { - font-style: italic -} - -b, strong { - font-weight: bolder -} - -small { - font-size: 80% -} - -sub, sup { - position: relative; - font-size: 75%; - line-height: 0; - vertical-align: baseline -} - -sub { - bottom: -.25em -} - -sup { - top: -.5em -} - -a { - color: #009688; - text-decoration: none; - background-color: transparent; - -webkit-text-decoration-skip: objects -} - -a:hover { - color: #004a43; - text-decoration: underline -} - -a:not([href]):not([tabindex]), a:not([href]):not([tabindex]):focus, a:not([href]):not([tabindex]):hover { - color: inherit; - text-decoration: none -} - -a:not([href]):not([tabindex]):focus { - outline: 0 -} - -code, kbd, pre, samp { - font-family: monospace, monospace; - font-size: 1em -} - -pre { - margin-top: 0; - margin-bottom: 1rem; - overflow: auto; - -ms-overflow-style: scrollbar -} - -figure { - margin: 0 0 1rem -} - -img { - vertical-align: middle; - border-style: none -} - -svg:not(:root) { - overflow: hidden -} - -table { - border-collapse: collapse -} - -caption { - padding-top: .75rem; - padding-bottom: .75rem; - color: #6c757d; - text-align: left; - caption-side: bottom -} - -th { - text-align: inherit -} - -label { - display: inline-block; - margin-bottom: .5rem -} - -button { - border-radius: 0 -} - -button:focus { - outline: 1px dotted; - outline: 5px auto -webkit-focus-ring-color -} - -button, input, optgroup, select, textarea { - margin: 0; - font-family: inherit; - font-size: inherit; - line-height: inherit -} - -button, input { - overflow: visible -} - -button, select { - text-transform: none -} - -[type=reset], [type=submit], button, html [type=button] { - -webkit-appearance: button -} - -[type=button]::-moz-focus-inner, [type=reset]::-moz-focus-inner, [type=submit]::-moz-focus-inner, button::-moz-focus-inner { - padding: 0; - border-style: none -} - -input[type=checkbox], input[type=radio] { - box-sizing: border-box; - padding: 0 -} - -input[type=date], input[type=datetime-local], input[type=month], input[type=time] { - -webkit-appearance: listbox -} - -textarea { - overflow: auto; - resize: vertical -} - -fieldset { - min-width: 0; - padding: 0; - margin: 0; - border: 0 -} - -legend { - display: block; - width: 100%; - max-width: 100%; - padding: 0; - margin-bottom: .5rem; - font-size: 1.5rem; - line-height: inherit; - color: inherit; - white-space: normal -} - -progress { - vertical-align: baseline -} - -[type=number]::-webkit-inner-spin-button, [type=number]::-webkit-outer-spin-button { - height: auto -} - -[type=search] { - outline-offset: -2px; - -webkit-appearance: none -} - -[type=search]::-webkit-search-cancel-button, [type=search]::-webkit-search-decoration { - -webkit-appearance: none -} - -::-webkit-file-upload-button { - font: inherit; - -webkit-appearance: button -} - -output { - display: inline-block -} - -summary { - display: list-item; - cursor: pointer -} - -template { - display: none -} - -[hidden] { - display: none !important -} - -.h1, .h2, .h3, .h4, .h5, .h6, h1, h2, h3, h4, h5, h6 { - margin-bottom: .5rem; - font-family: inherit; - font-weight: 400; - line-height: 1.2; - color: inherit -} - -.h1, h1 { - font-size: 2.5rem -} - -.h2, h2 { - font-size: 2rem -} - -.h3, h3 { - font-size: 1.75rem -} - -.h4, h4 { - font-size: 1.5rem -} - -.h5, h5 { - font-size: 1.25rem -} - -.h6, h6 { - font-size: 1rem -} - -.lead { - font-size: 1.25rem; - font-weight: 300 -} - -.display-1 { - font-size: 7rem -} - -.display-1, .display-2 { - font-weight: 300; - line-height: 1.2 -} - -.display-2 { - font-size: 3.5rem -} - -.display-3 { - font-size: 2.8125rem -} - -.display-3, .display-4 { - font-weight: 300; - line-height: 1.2 -} - -.display-4 { - font-size: 2.125rem -} - -hr { - margin-top: 1rem; - margin-bottom: 1rem; - border: 0; - border-top: 1px solid rgba(0, 0, 0, .1) -} - -.small, small { - font-size: 80%; - font-weight: 400 -} - -.mark, mark { - padding: .2em; - background-color: #fcf8e3 -} - -.list-inline, .list-unstyled { - padding-left: 0; - list-style: none -} - -.list-inline-item { - display: inline-block -} - -.list-inline-item:not(:last-child) { - margin-right: .5rem -} - -.initialism { - font-size: 90%; - text-transform: uppercase -} - -.blockquote { - margin-bottom: 1rem; - font-size: 1.25rem -} - -.blockquote-footer { - display: block; - font-size: 80%; - color: #6c757d -} - -.blockquote-footer:before { - content: "\2014 \00A0" -} - -.img-fluid, .img-thumbnail { - max-width: 100%; - height: auto -} - -.img-thumbnail { - padding: .25rem; - background-color: #fafafa; - border: 1px solid #dee2e6; - border-radius: .125rem; - box-shadow: 0 1px 2px rgba(0, 0, 0, .075) -} - -.figure { - display: inline-block -} - -.figure-img { - margin-bottom: .5rem; - line-height: 1 -} - -.figure-caption { - font-size: 90%; - color: #6c757d -} - -code, kbd, pre, samp { - font-family: SFMono-Regular, Menlo, Monaco, Consolas, Liberation Mono, Courier New, monospace -} - -code { - font-size: 87.5%; - color: #e91e63; - word-break: break-word -} - -a > code { - color: inherit -} - -kbd { - padding: .2rem .4rem; - font-size: 87.5%; - color: #fff; - background-color: #212529; - border-radius: .0625rem; - box-shadow: inset 0 -.1rem 0 rgba(0, 0, 0, .25) -} - -kbd kbd { - padding: 0; - font-size: 100%; - font-weight: 700; - box-shadow: none -} - -pre { - display: block; - font-size: 87.5%; - color: #212529 -} - -pre code { - font-size: inherit; - color: inherit; - word-break: normal -} - -.pre-scrollable { - max-height: 340px; - overflow-y: scroll -} - -.container { - width: 100%; - padding-right: 15px; - padding-left: 15px; - margin-right: auto; - margin-left: auto -} - -@media (min-width: 576px) { - .container { - max-width: 540px - } -} - -@media (min-width: 768px) { - .container { - max-width: 720px - } -} - -@media (min-width: 992px) { - .container { - max-width: 960px - } -} - -@media (min-width: 1200px) { - .container { - max-width: 1140px - } -} - -.container-fluid { - width: 100%; - padding-right: 15px; - padding-left: 15px; - margin-right: auto; - margin-left: auto -} - -.row { - display: flex; - flex-wrap: wrap; - margin-right: -15px; - margin-left: -15px -} - -.no-gutters { - margin-right: 0; - margin-left: 0 -} - -.no-gutters > .col, .no-gutters > [class*=col-] { - padding-right: 0; - padding-left: 0 -} - -.col, .col-1, .col-2, .col-3, .col-4, .col-5, .col-6, .col-7, .col-8, .col-9, .col-10, .col-11, .col-12, .col-auto, .col-lg, .col-lg-1, .col-lg-2, .col-lg-3, .col-lg-4, .col-lg-5, .col-lg-6, .col-lg-7, .col-lg-8, .col-lg-9, .col-lg-10, .col-lg-11, .col-lg-12, .col-lg-auto, .col-md, .col-md-1, .col-md-2, .col-md-3, .col-md-4, .col-md-5, .col-md-6, .col-md-7, .col-md-8, .col-md-9, .col-md-10, .col-md-11, .col-md-12, .col-md-auto, .col-sm, .col-sm-1, .col-sm-2, .col-sm-3, .col-sm-4, .col-sm-5, .col-sm-6, .col-sm-7, .col-sm-8, .col-sm-9, .col-sm-10, .col-sm-11, .col-sm-12, .col-sm-auto, .col-xl, .col-xl-1, .col-xl-2, .col-xl-3, .col-xl-4, .col-xl-5, .col-xl-6, .col-xl-7, .col-xl-8, .col-xl-9, .col-xl-10, .col-xl-11, .col-xl-12, .col-xl-auto { - position: relative; - width: 100%; - min-height: 1px; - padding-right: 15px; - padding-left: 15px -} - -.col { - flex-basis: 0; - flex-grow: 1; - max-width: 100% -} - -.col-auto { - flex: 0 0 auto; - width: auto; - max-width: none -} - -.col-1 { - flex: 0 0 8.33333%; - max-width: 8.33333% -} - -.col-2 { - flex: 0 0 16.66667%; - max-width: 16.66667% -} - -.col-3 { - flex: 0 0 25%; - max-width: 25% -} - -.col-4 { - flex: 0 0 33.33333%; - max-width: 33.33333% -} - -.col-5 { - flex: 0 0 41.66667%; - max-width: 41.66667% -} - -.col-6 { - flex: 0 0 50%; - max-width: 50% -} - -.col-7 { - flex: 0 0 58.33333%; - max-width: 58.33333% -} - -.col-8 { - flex: 0 0 66.66667%; - max-width: 66.66667% -} - -.col-9 { - flex: 0 0 75%; - max-width: 75% -} - -.col-10 { - flex: 0 0 83.33333%; - max-width: 83.33333% -} - -.col-11 { - flex: 0 0 91.66667%; - max-width: 91.66667% -} - -.col-12 { - flex: 0 0 100%; - max-width: 100% -} - -.order-first { - order: -1 -} - -.order-last { - order: 13 -} - -.order-0 { - order: 0 -} - -.order-1 { - order: 1 -} - -.order-2 { - order: 2 -} - -.order-3 { - order: 3 -} - -.order-4 { - order: 4 -} - -.order-5 { - order: 5 -} - -.order-6 { - order: 6 -} - -.order-7 { - order: 7 -} - -.order-8 { - order: 8 -} - -.order-9 { - order: 9 -} - -.order-10 { - order: 10 -} - -.order-11 { - order: 11 -} - -.order-12 { - order: 12 -} - -.offset-1 { - margin-left: 8.33333% -} - -.offset-2 { - margin-left: 16.66667% -} - -.offset-3 { - margin-left: 25% -} - -.offset-4 { - margin-left: 33.33333% -} - -.offset-5 { - margin-left: 41.66667% -} - -.offset-6 { - margin-left: 50% -} - -.offset-7 { - margin-left: 58.33333% -} - -.offset-8 { - margin-left: 66.66667% -} - -.offset-9 { - margin-left: 75% -} - -.offset-10 { - margin-left: 83.33333% -} - -.offset-11 { - margin-left: 91.66667% -} - -@media (min-width: 576px) { - .col-sm { - flex-basis: 0; - flex-grow: 1; - max-width: 100% - } - - .col-sm-auto { - flex: 0 0 auto; - width: auto; - max-width: none - } - - .col-sm-1 { - flex: 0 0 8.33333%; - max-width: 8.33333% - } - - .col-sm-2 { - flex: 0 0 16.66667%; - max-width: 16.66667% - } - - .col-sm-3 { - flex: 0 0 25%; - max-width: 25% - } - - .col-sm-4 { - flex: 0 0 33.33333%; - max-width: 33.33333% - } - - .col-sm-5 { - flex: 0 0 41.66667%; - max-width: 41.66667% - } - - .col-sm-6 { - flex: 0 0 50%; - max-width: 50% - } - - .col-sm-7 { - flex: 0 0 58.33333%; - max-width: 58.33333% - } - - .col-sm-8 { - flex: 0 0 66.66667%; - max-width: 66.66667% - } - - .col-sm-9 { - flex: 0 0 75%; - max-width: 75% - } - - .col-sm-10 { - flex: 0 0 83.33333%; - max-width: 83.33333% - } - - .col-sm-11 { - flex: 0 0 91.66667%; - max-width: 91.66667% - } - - .col-sm-12 { - flex: 0 0 100%; - max-width: 100% - } - - .order-sm-first { - order: -1 - } - - .order-sm-last { - order: 13 - } - - .order-sm-0 { - order: 0 - } - - .order-sm-1 { - order: 1 - } - - .order-sm-2 { - order: 2 - } - - .order-sm-3 { - order: 3 - } - - .order-sm-4 { - order: 4 - } - - .order-sm-5 { - order: 5 - } - - .order-sm-6 { - order: 6 - } - - .order-sm-7 { - order: 7 - } - - .order-sm-8 { - order: 8 - } - - .order-sm-9 { - order: 9 - } - - .order-sm-10 { - order: 10 - } - - .order-sm-11 { - order: 11 - } - - .order-sm-12 { - order: 12 - } - - .offset-sm-0 { - margin-left: 0 - } - - .offset-sm-1 { - margin-left: 8.33333% - } - - .offset-sm-2 { - margin-left: 16.66667% - } - - .offset-sm-3 { - margin-left: 25% - } - - .offset-sm-4 { - margin-left: 33.33333% - } - - .offset-sm-5 { - margin-left: 41.66667% - } - - .offset-sm-6 { - margin-left: 50% - } - - .offset-sm-7 { - margin-left: 58.33333% - } - - .offset-sm-8 { - margin-left: 66.66667% - } - - .offset-sm-9 { - margin-left: 75% - } - - .offset-sm-10 { - margin-left: 83.33333% - } - - .offset-sm-11 { - margin-left: 91.66667% - } -} - -@media (min-width: 768px) { - .col-md { - flex-basis: 0; - flex-grow: 1; - max-width: 100% - } - - .col-md-auto { - flex: 0 0 auto; - width: auto; - max-width: none - } - - .col-md-1 { - flex: 0 0 8.33333%; - max-width: 8.33333% - } - - .col-md-2 { - flex: 0 0 16.66667%; - max-width: 16.66667% - } - - .col-md-3 { - flex: 0 0 25%; - max-width: 25% - } - - .col-md-4 { - flex: 0 0 33.33333%; - max-width: 33.33333% - } - - .col-md-5 { - flex: 0 0 41.66667%; - max-width: 41.66667% - } - - .col-md-6 { - flex: 0 0 50%; - max-width: 50% - } - - .col-md-7 { - flex: 0 0 58.33333%; - max-width: 58.33333% - } - - .col-md-8 { - flex: 0 0 66.66667%; - max-width: 66.66667% - } - - .col-md-9 { - flex: 0 0 75%; - max-width: 75% - } - - .col-md-10 { - flex: 0 0 83.33333%; - max-width: 83.33333% - } - - .col-md-11 { - flex: 0 0 91.66667%; - max-width: 91.66667% - } - - .col-md-12 { - flex: 0 0 100%; - max-width: 100% - } - - .order-md-first { - order: -1 - } - - .order-md-last { - order: 13 - } - - .order-md-0 { - order: 0 - } - - .order-md-1 { - order: 1 - } - - .order-md-2 { - order: 2 - } - - .order-md-3 { - order: 3 - } - - .order-md-4 { - order: 4 - } - - .order-md-5 { - order: 5 - } - - .order-md-6 { - order: 6 - } - - .order-md-7 { - order: 7 - } - - .order-md-8 { - order: 8 - } - - .order-md-9 { - order: 9 - } - - .order-md-10 { - order: 10 - } - - .order-md-11 { - order: 11 - } - - .order-md-12 { - order: 12 - } - - .offset-md-0 { - margin-left: 0 - } - - .offset-md-1 { - margin-left: 8.33333% - } - - .offset-md-2 { - margin-left: 16.66667% - } - - .offset-md-3 { - margin-left: 25% - } - - .offset-md-4 { - margin-left: 33.33333% - } - - .offset-md-5 { - margin-left: 41.66667% - } - - .offset-md-6 { - margin-left: 50% - } - - .offset-md-7 { - margin-left: 58.33333% - } - - .offset-md-8 { - margin-left: 66.66667% - } - - .offset-md-9 { - margin-left: 75% - } - - .offset-md-10 { - margin-left: 83.33333% - } - - .offset-md-11 { - margin-left: 91.66667% - } -} - -@media (min-width: 992px) { - .col-lg { - flex-basis: 0; - flex-grow: 1; - max-width: 100% - } - - .col-lg-auto { - flex: 0 0 auto; - width: auto; - max-width: none - } - - .col-lg-1 { - flex: 0 0 8.33333%; - max-width: 8.33333% - } - - .col-lg-2 { - flex: 0 0 16.66667%; - max-width: 16.66667% - } - - .col-lg-3 { - flex: 0 0 25%; - max-width: 25% - } - - .col-lg-4 { - flex: 0 0 33.33333%; - max-width: 33.33333% - } - - .col-lg-5 { - flex: 0 0 41.66667%; - max-width: 41.66667% - } - - .col-lg-6 { - flex: 0 0 50%; - max-width: 50% - } - - .col-lg-7 { - flex: 0 0 58.33333%; - max-width: 58.33333% - } - - .col-lg-8 { - flex: 0 0 66.66667%; - max-width: 66.66667% - } - - .col-lg-9 { - flex: 0 0 75%; - max-width: 75% - } - - .col-lg-10 { - flex: 0 0 83.33333%; - max-width: 83.33333% - } - - .col-lg-11 { - flex: 0 0 91.66667%; - max-width: 91.66667% - } - - .col-lg-12 { - flex: 0 0 100%; - max-width: 100% - } - - .order-lg-first { - order: -1 - } - - .order-lg-last { - order: 13 - } - - .order-lg-0 { - order: 0 - } - - .order-lg-1 { - order: 1 - } - - .order-lg-2 { - order: 2 - } - - .order-lg-3 { - order: 3 - } - - .order-lg-4 { - order: 4 - } - - .order-lg-5 { - order: 5 - } - - .order-lg-6 { - order: 6 - } - - .order-lg-7 { - order: 7 - } - - .order-lg-8 { - order: 8 - } - - .order-lg-9 { - order: 9 - } - - .order-lg-10 { - order: 10 - } - - .order-lg-11 { - order: 11 - } - - .order-lg-12 { - order: 12 - } - - .offset-lg-0 { - margin-left: 0 - } - - .offset-lg-1 { - margin-left: 8.33333% - } - - .offset-lg-2 { - margin-left: 16.66667% - } - - .offset-lg-3 { - margin-left: 25% - } - - .offset-lg-4 { - margin-left: 33.33333% - } - - .offset-lg-5 { - margin-left: 41.66667% - } - - .offset-lg-6 { - margin-left: 50% - } - - .offset-lg-7 { - margin-left: 58.33333% - } - - .offset-lg-8 { - margin-left: 66.66667% - } - - .offset-lg-9 { - margin-left: 75% - } - - .offset-lg-10 { - margin-left: 83.33333% - } - - .offset-lg-11 { - margin-left: 91.66667% - } -} - -@media (min-width: 1200px) { - .col-xl { - flex-basis: 0; - flex-grow: 1; - max-width: 100% - } - - .col-xl-auto { - flex: 0 0 auto; - width: auto; - max-width: none - } - - .col-xl-1 { - flex: 0 0 8.33333%; - max-width: 8.33333% - } - - .col-xl-2 { - flex: 0 0 16.66667%; - max-width: 16.66667% - } - - .col-xl-3 { - flex: 0 0 25%; - max-width: 25% - } - - .col-xl-4 { - flex: 0 0 33.33333%; - max-width: 33.33333% - } - - .col-xl-5 { - flex: 0 0 41.66667%; - max-width: 41.66667% - } - - .col-xl-6 { - flex: 0 0 50%; - max-width: 50% - } - - .col-xl-7 { - flex: 0 0 58.33333%; - max-width: 58.33333% - } - - .col-xl-8 { - flex: 0 0 66.66667%; - max-width: 66.66667% - } - - .col-xl-9 { - flex: 0 0 75%; - max-width: 75% - } - - .col-xl-10 { - flex: 0 0 83.33333%; - max-width: 83.33333% - } - - .col-xl-11 { - flex: 0 0 91.66667%; - max-width: 91.66667% - } - - .col-xl-12 { - flex: 0 0 100%; - max-width: 100% - } - - .order-xl-first { - order: -1 - } - - .order-xl-last { - order: 13 - } - - .order-xl-0 { - order: 0 - } - - .order-xl-1 { - order: 1 - } - - .order-xl-2 { - order: 2 - } - - .order-xl-3 { - order: 3 - } - - .order-xl-4 { - order: 4 - } - - .order-xl-5 { - order: 5 - } - - .order-xl-6 { - order: 6 - } - - .order-xl-7 { - order: 7 - } - - .order-xl-8 { - order: 8 - } - - .order-xl-9 { - order: 9 - } - - .order-xl-10 { - order: 10 - } - - .order-xl-11 { - order: 11 - } - - .order-xl-12 { - order: 12 - } - - .offset-xl-0 { - margin-left: 0 - } - - .offset-xl-1 { - margin-left: 8.33333% - } - - .offset-xl-2 { - margin-left: 16.66667% - } - - .offset-xl-3 { - margin-left: 25% - } - - .offset-xl-4 { - margin-left: 33.33333% - } - - .offset-xl-5 { - margin-left: 41.66667% - } - - .offset-xl-6 { - margin-left: 50% - } - - .offset-xl-7 { - margin-left: 58.33333% - } - - .offset-xl-8 { - margin-left: 66.66667% - } - - .offset-xl-9 { - margin-left: 75% - } - - .offset-xl-10 { - margin-left: 83.33333% - } - - .offset-xl-11 { - margin-left: 91.66667% - } -} - -.table { - width: 100%; - max-width: 100%; - margin-bottom: 1rem; - background-color: transparent -} - -.table td, .table th { - padding: .75rem; - vertical-align: top; - border-top: 1px solid rgba(0, 0, 0, .06) -} - -.table thead th { - vertical-align: bottom; - border-bottom: 2px solid rgba(0, 0, 0, .06) -} - -.table tbody + tbody { - border-top: 2px solid rgba(0, 0, 0, .06) -} - -.table .table { - background-color: #fafafa -} - -.table-sm td, .table-sm th { - padding: .3rem -} - -.table-bordered, .table-bordered td, .table-bordered th { - border: 1px solid rgba(0, 0, 0, .06) -} - -.table-bordered thead td, .table-bordered thead th { - border-bottom-width: 2px -} - -.table-striped tbody tr:nth-of-type(odd) { - background-color: rgba(0, 0, 0, .05) -} - -.table-hover tbody tr:hover { - background-color: rgba(0, 0, 0, .075) -} - -.table-primary, .table-primary > td, .table-primary > th { - background-color: #b8e2de -} - -.table-hover .table-primary:hover, .table-hover .table-primary:hover > td, .table-hover .table-primary:hover > th { - background-color: #a6dbd6 -} - -.table-secondary, .table-secondary > td, .table-secondary > th { - background-color: #d6d8db -} - -.table-hover .table-secondary:hover, .table-hover .table-secondary:hover > td, .table-hover .table-secondary:hover > th { - background-color: #c8cbcf -} - -.table-success, .table-success > td, .table-success > th { - background-color: #cde9ce -} - -.table-hover .table-success:hover, .table-hover .table-success:hover > td, .table-hover .table-success:hover > th { - background-color: #bbe1bd -} - -.table-info, .table-info > td, .table-info > th { - background-color: #b8e7fc -} - -.table-hover .table-info:hover, .table-hover .table-info:hover > td, .table-hover .table-info:hover > th { - background-color: #a0dffb -} - -.table-warning, .table-warning > td, .table-warning > th { - background-color: #ffd0c1 -} - -.table-hover .table-warning:hover, .table-hover .table-warning:hover > td, .table-hover .table-warning:hover > th { - background-color: #ffbda8 -} - -.table-danger, .table-danger > td, .table-danger > th { - background-color: #fccac7 -} - -.table-hover .table-danger:hover, .table-hover .table-danger:hover > td, .table-hover .table-danger:hover > th { - background-color: #fbb3af -} - -.table-light, .table-light > td, .table-light > th { - background-color: #fcfcfc -} - -.table-hover .table-light:hover, .table-hover .table-light:hover > td, .table-hover .table-light:hover > th { - background-color: #efefef -} - -.table-dark, .table-dark > td, .table-dark > th { - background-color: #cacaca -} - -.table-hover .table-dark:hover, .table-hover .table-dark:hover > td, .table-hover .table-dark:hover > th { - background-color: #bdbdbd -} - -.table-active, .table-active > td, .table-active > th, .table-hover .table-active:hover, .table-hover .table-active:hover > td, .table-hover .table-active:hover > th { - background-color: rgba(0, 0, 0, .075) -} - -.table .thead-dark th { - color: #fafafa; - background-color: #212529; - border-color: #32383e -} - -.table .thead-light th { - color: #495057; - background-color: #e9ecef; - border-color: rgba(0, 0, 0, .06) -} - -.table-dark { - color: #fafafa; - background-color: #212529 -} - -.table-dark td, .table-dark th, .table-dark thead th { - border-color: #32383e -} - -.table-dark.table-bordered { - border: 0 -} - -.table-dark.table-striped tbody tr:nth-of-type(odd) { - background-color: hsla(0, 0%, 100%, .05) -} - -.table-dark.table-hover tbody tr:hover { - background-color: hsla(0, 0%, 100%, .075) -} - -@media (max-width: 575.98px) { - .table-responsive-sm { - display: block; - width: 100%; - overflow-x: auto; - -webkit-overflow-scrolling: touch; - -ms-overflow-style: -ms-autohiding-scrollbar - } - - .table-responsive-sm > .table-bordered { - border: 0 - } -} - -@media (max-width: 767.98px) { - .table-responsive-md { - display: block; - width: 100%; - overflow-x: auto; - -webkit-overflow-scrolling: touch; - -ms-overflow-style: -ms-autohiding-scrollbar - } - - .table-responsive-md > .table-bordered { - border: 0 - } -} - -@media (max-width: 991.98px) { - .table-responsive-lg { - display: block; - width: 100%; - overflow-x: auto; - -webkit-overflow-scrolling: touch; - -ms-overflow-style: -ms-autohiding-scrollbar - } - - .table-responsive-lg > .table-bordered { - border: 0 - } -} - -@media (max-width: 1199.98px) { - .table-responsive-xl { - display: block; - width: 100%; - overflow-x: auto; - -webkit-overflow-scrolling: touch; - -ms-overflow-style: -ms-autohiding-scrollbar - } - - .table-responsive-xl > .table-bordered { - border: 0 - } -} - -.table-responsive { - display: block; - width: 100%; - overflow-x: auto; - -webkit-overflow-scrolling: touch; - -ms-overflow-style: -ms-autohiding-scrollbar -} - -.table-responsive > .table-bordered { - border: 0 -} - -.custom-file-control, .form-control { - display: block; - width: 100%; - padding: .4375rem 0; - font-size: 1rem; - line-height: 1.5; - color: #495057; - background-color: transparent; - background-clip: padding-box; - border: 1px solid rgba(0, 0, 0, .26); - border-radius: 0; - box-shadow: none; - transition: border-color .15s ease-in-out, box-shadow .15s ease-in-out -} - -.custom-file-control::-ms-expand, .form-control::-ms-expand { - background-color: transparent; - border: 0 -} - -.custom-file-control:focus, .form-control:focus { - color: #495057; - background-color: transparent; - border-color: #17ffe9; - outline: 0; - box-shadow: none, 0 0 0 .2rem rgba(0, 150, 136, .25) -} - -.custom-file-control::placeholder, .form-control::placeholder { - color: #6c757d; - opacity: 1 -} - -.custom-file-control:disabled, .form-control:disabled, .form-control[readonly], [readonly].custom-file-control { - background-color: #e9ecef; - opacity: 1 -} - -select.custom-file-control:not([size]):not([multiple]), select.form-control:not([size]):not([multiple]) { - height: calc(2.4375rem + 2px) -} - -select.custom-file-control:focus::-ms-value, select.form-control:focus::-ms-value { - color: #495057; - background-color: transparent -} - -.form-control-file, .form-control-range { - display: block; - width: 100% -} - -.col-form-label { - padding-top: calc(.4375rem + 1px); - padding-bottom: calc(.4375rem + 1px); - margin-bottom: 0; - font-size: inherit; - line-height: 1.5 -} - -.col-form-label-lg { - padding-top: calc(.5625rem + 1px); - padding-bottom: calc(.5625rem + 1px); - font-size: 1.25rem; - line-height: 1.5 -} - -.col-form-label-sm { - padding-top: calc(.25rem + 1px); - padding-bottom: calc(.25rem + 1px); - font-size: .875rem; - line-height: 1.5 -} - -.form-control-plaintext { - display: block; - width: 100%; - padding-top: .4375rem; - padding-bottom: .4375rem; - margin-bottom: 0; - line-height: 1.5; - background-color: transparent; - border: solid transparent; - border-width: 1px 0 -} - -.form-control-plaintext.form-control-lg, .form-control-plaintext.form-control-sm, .input-group-lg > .form-control-plaintext.custom-file-control, .input-group-lg > .form-control-plaintext.form-control, .input-group-lg > .input-group-append > .form-control-plaintext.btn, .input-group-lg > .input-group-append > .form-control-plaintext.custom-file-control:before, .input-group-lg > .input-group-append > .form-control-plaintext.input-group-text, .input-group-lg > .input-group-prepend > .form-control-plaintext.btn, .input-group-lg > .input-group-prepend > .form-control-plaintext.custom-file-control:before, .input-group-lg > .input-group-prepend > .form-control-plaintext.input-group-text, .input-group-sm > .form-control-plaintext.custom-file-control, .input-group-sm > .form-control-plaintext.form-control, .input-group-sm > .input-group-append > .form-control-plaintext.btn, .input-group-sm > .input-group-append > .form-control-plaintext.custom-file-control:before, .input-group-sm > .input-group-append > .form-control-plaintext.input-group-text, .input-group-sm > .input-group-prepend > .form-control-plaintext.btn, .input-group-sm > .input-group-prepend > .form-control-plaintext.custom-file-control:before, .input-group-sm > .input-group-prepend > .form-control-plaintext.input-group-text { - padding-right: 0; - padding-left: 0 -} - -.form-control-sm, .input-group-sm > .custom-file-control, .input-group-sm > .form-control, .input-group-sm > .input-group-append > .btn, .input-group-sm > .input-group-append > .custom-file-control:before, .input-group-sm > .input-group-append > .input-group-text, .input-group-sm > .input-group-prepend > .btn, .input-group-sm > .input-group-prepend > .custom-file-control:before, .input-group-sm > .input-group-prepend > .input-group-text { - padding: .25rem 0; - font-size: .875rem; - line-height: 1.5; - border-radius: .0625rem -} - -.input-group-sm > .input-group-append > select.btn:not([size]):not([multiple]), .input-group-sm > .input-group-append > select.custom-file-control:not([size]):not([multiple]):before, .input-group-sm > .input-group-append > select.input-group-text:not([size]):not([multiple]), .input-group-sm > .input-group-prepend > select.btn:not([size]):not([multiple]), .input-group-sm > .input-group-prepend > select.custom-file-control:not([size]):not([multiple]):before, .input-group-sm > .input-group-prepend > select.input-group-text:not([size]):not([multiple]), .input-group-sm > select.custom-file-control:not([size]):not([multiple]), .input-group-sm > select.form-control:not([size]):not([multiple]), select.form-control-sm:not([size]):not([multiple]) { - height: calc(2.125rem + 2px) -} - -.form-control-lg, .input-group-lg > .custom-file-control, .input-group-lg > .form-control, .input-group-lg > .input-group-append > .btn, .input-group-lg > .input-group-append > .custom-file-control:before, .input-group-lg > .input-group-append > .input-group-text, .input-group-lg > .input-group-prepend > .btn, .input-group-lg > .input-group-prepend > .custom-file-control:before, .input-group-lg > .input-group-prepend > .input-group-text { - padding: .5625rem 0; - font-size: 1.25rem; - line-height: 1.5; - border-radius: .3rem -} - -.input-group-lg > .input-group-append > select.btn:not([size]):not([multiple]), .input-group-lg > .input-group-append > select.custom-file-control:not([size]):not([multiple]):before, .input-group-lg > .input-group-append > select.input-group-text:not([size]):not([multiple]), .input-group-lg > .input-group-prepend > select.btn:not([size]):not([multiple]), .input-group-lg > .input-group-prepend > select.custom-file-control:not([size]):not([multiple]):before, .input-group-lg > .input-group-prepend > select.input-group-text:not([size]):not([multiple]), .input-group-lg > select.custom-file-control:not([size]):not([multiple]), .input-group-lg > select.form-control:not([size]):not([multiple]), select.form-control-lg:not([size]):not([multiple]) { - height: calc(2.875rem + 2px) -} - -.form-group { - margin-bottom: 1rem -} - -.form-text { - display: block; - margin-top: .25rem -} - -.form-row { - display: flex; - flex-wrap: wrap; - margin-right: -5px; - margin-left: -5px -} - -.form-row > .col, .form-row > [class*=col-] { - padding-right: 5px; - padding-left: 5px -} - -.form-check { - position: relative; - display: block; - padding-left: 1.25rem -} - -.form-check-input { - position: absolute; - margin-top: .3rem; - margin-left: -1.25rem -} - -.form-check-input:disabled ~ .form-check-label { - color: #6c757d -} - -.form-check-label { - margin-bottom: 0 -} - -.form-check-inline { - display: inline-flex; - align-items: center; - padding-left: 0; - margin-right: .75rem -} - -.form-check-inline .form-check-input { - position: static; - margin-top: 0; - margin-right: .3125rem; - margin-left: 0 -} - -.valid-feedback { - display: none; - width: 100%; - margin-top: .25rem; - font-size: 80%; - color: #4caf50 -} - -.valid-tooltip { - position: absolute; - top: 100%; - z-index: 5; - display: none; - max-width: 100%; - padding: .5rem; - margin-top: .1rem; - font-size: .875rem; - line-height: 1; - color: #fff; - background-color: rgba(76, 175, 80, .8); - border-radius: .2rem -} - -.custom-select.is-valid, .form-control.is-valid, .is-valid.custom-file-control, .was-validated .custom-file-control:valid, .was-validated .custom-select:valid, .was-validated .form-control:valid { - border-color: #4caf50 -} - -.custom-select.is-valid:focus, .form-control.is-valid:focus, .is-valid.custom-file-control:focus, .was-validated .custom-file-control:valid:focus, .was-validated .custom-select:valid:focus, .was-validated .form-control:valid:focus { - border-color: #4caf50; - box-shadow: 0 0 0 .2rem rgba(76, 175, 80, .25) -} - -.custom-select.is-valid ~ .valid-feedback, .custom-select.is-valid ~ .valid-tooltip, .form-control.is-valid ~ .valid-feedback, .form-control.is-valid ~ .valid-tooltip, .is-valid.custom-file-control ~ .valid-feedback, .is-valid.custom-file-control ~ .valid-tooltip, .was-validated .custom-file-control:valid ~ .valid-feedback, .was-validated .custom-file-control:valid ~ .valid-tooltip, .was-validated .custom-select:valid ~ .valid-feedback, .was-validated .custom-select:valid ~ .valid-tooltip, .was-validated .form-control:valid ~ .valid-feedback, .was-validated .form-control:valid ~ .valid-tooltip { - display: block -} - -.form-check-input.is-valid ~ .form-check-label, .was-validated .form-check-input:valid ~ .form-check-label { - color: #4caf50 -} - -.form-check-input.is-valid ~ .valid-feedback, .form-check-input.is-valid ~ .valid-tooltip, .was-validated .form-check-input:valid ~ .valid-feedback, .was-validated .form-check-input:valid ~ .valid-tooltip { - display: block -} - -.custom-control-input.is-valid ~ .custom-control-label, .was-validated .custom-control-input:valid ~ .custom-control-label { - color: #4caf50 -} - -.custom-control-input.is-valid ~ .custom-control-label:before, .was-validated .custom-control-input:valid ~ .custom-control-label:before { - background-color: #a3d7a5 -} - -.custom-control-input.is-valid ~ .valid-feedback, .custom-control-input.is-valid ~ .valid-tooltip, .was-validated .custom-control-input:valid ~ .valid-feedback, .was-validated .custom-control-input:valid ~ .valid-tooltip { - display: block -} - -.custom-control-input.is-valid:checked ~ .custom-control-label:before, .was-validated .custom-control-input:valid:checked ~ .custom-control-label:before { - background-color: #6ec071 -} - -.custom-control-input.is-valid:focus ~ .custom-control-label:before, .was-validated .custom-control-input:valid:focus ~ .custom-control-label:before { - box-shadow: 0 0 0 1px #fafafa, 0 0 0 .2rem rgba(76, 175, 80, .25) -} - -.custom-file-input.is-valid ~ .custom-file-label, .was-validated .custom-file-input:valid ~ .custom-file-label { - border-color: #4caf50 -} - -.custom-file-input.is-valid ~ .custom-file-label:before, .was-validated .custom-file-input:valid ~ .custom-file-label:before { - border-color: inherit -} - -.custom-file-input.is-valid ~ .valid-feedback, .custom-file-input.is-valid ~ .valid-tooltip, .was-validated .custom-file-input:valid ~ .valid-feedback, .was-validated .custom-file-input:valid ~ .valid-tooltip { - display: block -} - -.custom-file-input.is-valid:focus ~ .custom-file-label, .was-validated .custom-file-input:valid:focus ~ .custom-file-label { - box-shadow: 0 0 0 .2rem rgba(76, 175, 80, .25) -} - -.invalid-feedback { - display: none; - width: 100%; - margin-top: .25rem; - font-size: 80%; - color: #f44336 -} - -.invalid-tooltip { - position: absolute; - top: 100%; - z-index: 5; - display: none; - max-width: 100%; - padding: .5rem; - margin-top: .1rem; - font-size: .875rem; - line-height: 1; - color: #fff; - background-color: rgba(244, 67, 54, .8); - border-radius: .2rem -} - -.custom-select.is-invalid, .form-control.is-invalid, .is-invalid.custom-file-control, .was-validated .custom-file-control:invalid, .was-validated .custom-select:invalid, .was-validated .form-control:invalid { - border-color: #f44336 -} - -.custom-select.is-invalid:focus, .form-control.is-invalid:focus, .is-invalid.custom-file-control:focus, .was-validated .custom-file-control:invalid:focus, .was-validated .custom-select:invalid:focus, .was-validated .form-control:invalid:focus { - border-color: #f44336; - box-shadow: 0 0 0 .2rem rgba(244, 67, 54, .25) -} - -.custom-select.is-invalid ~ .invalid-feedback, .custom-select.is-invalid ~ .invalid-tooltip, .form-control.is-invalid ~ .invalid-feedback, .form-control.is-invalid ~ .invalid-tooltip, .is-invalid.custom-file-control ~ .invalid-feedback, .is-invalid.custom-file-control ~ .invalid-tooltip, .was-validated .custom-file-control:invalid ~ .invalid-feedback, .was-validated .custom-file-control:invalid ~ .invalid-tooltip, .was-validated .custom-select:invalid ~ .invalid-feedback, .was-validated .custom-select:invalid ~ .invalid-tooltip, .was-validated .form-control:invalid ~ .invalid-feedback, .was-validated .form-control:invalid ~ .invalid-tooltip { - display: block -} - -.form-check-input.is-invalid ~ .form-check-label, .was-validated .form-check-input:invalid ~ .form-check-label { - color: #f44336 -} - -.form-check-input.is-invalid ~ .invalid-feedback, .form-check-input.is-invalid ~ .invalid-tooltip, .was-validated .form-check-input:invalid ~ .invalid-feedback, .was-validated .form-check-input:invalid ~ .invalid-tooltip { - display: block -} - -.custom-control-input.is-invalid ~ .custom-control-label, .was-validated .custom-control-input:invalid ~ .custom-control-label { - color: #f44336 -} - -.custom-control-input.is-invalid ~ .custom-control-label:before, .was-validated .custom-control-input:invalid ~ .custom-control-label:before { - background-color: #fbb4af -} - -.custom-control-input.is-invalid ~ .invalid-feedback, .custom-control-input.is-invalid ~ .invalid-tooltip, .was-validated .custom-control-input:invalid ~ .invalid-feedback, .was-validated .custom-control-input:invalid ~ .invalid-tooltip { - display: block -} - -.custom-control-input.is-invalid:checked ~ .custom-control-label:before, .was-validated .custom-control-input:invalid:checked ~ .custom-control-label:before { - background-color: #f77066 -} - -.custom-control-input.is-invalid:focus ~ .custom-control-label:before, .was-validated .custom-control-input:invalid:focus ~ .custom-control-label:before { - box-shadow: 0 0 0 1px #fafafa, 0 0 0 .2rem rgba(244, 67, 54, .25) -} - -.custom-file-input.is-invalid ~ .custom-file-label, .was-validated .custom-file-input:invalid ~ .custom-file-label { - border-color: #f44336 -} - -.custom-file-input.is-invalid ~ .custom-file-label:before, .was-validated .custom-file-input:invalid ~ .custom-file-label:before { - border-color: inherit -} - -.custom-file-input.is-invalid ~ .invalid-feedback, .custom-file-input.is-invalid ~ .invalid-tooltip, .was-validated .custom-file-input:invalid ~ .invalid-feedback, .was-validated .custom-file-input:invalid ~ .invalid-tooltip { - display: block -} - -.custom-file-input.is-invalid:focus ~ .custom-file-label, .was-validated .custom-file-input:invalid:focus ~ .custom-file-label { - box-shadow: 0 0 0 .2rem rgba(244, 67, 54, .25) -} - -.form-inline { - display: flex; - flex-flow: row wrap; - align-items: center -} - -.form-inline .form-check { - width: 100% -} - -@media (min-width: 576px) { - .form-inline label { - justify-content: center - } - - .form-inline .form-group, .form-inline label { - display: flex; - align-items: center; - margin-bottom: 0 - } - - .form-inline .form-group { - flex: 0 0 auto; - flex-flow: row wrap - } - - .form-inline .custom-file-control, .form-inline .form-control { - display: inline-block; - width: auto; - vertical-align: middle - } - - .form-inline .form-control-plaintext { - display: inline-block - } - - .form-inline .input-group { - width: auto - } - - .form-inline .form-check { - display: flex; - align-items: center; - justify-content: center; - width: auto; - padding-left: 0 - } - - .form-inline .form-check-input { - position: relative; - margin-top: 0; - margin-right: .25rem; - margin-left: 0 - } - - .form-inline .custom-control { - align-items: center; - justify-content: center - } - - .form-inline .custom-control-label { - margin-bottom: 0 - } -} - -.btn, .custom-file-control:before { - display: inline-block; - font-weight: 500; - text-align: center; - white-space: nowrap; - vertical-align: middle; - user-select: none; - border: 1px solid transparent; - padding: .46875rem 1rem; - font-size: 1rem; - line-height: 1.5; - border-radius: .125rem; - transition: color .15s ease-in-out, background-color .15s ease-in-out, border-color .15s ease-in-out, box-shadow .15s ease-in-out -} - -.btn:focus, .btn:hover, .custom-file-control:focus:before, .custom-file-control:hover:before { - text-decoration: none -} - -.btn.focus, .btn:focus, .custom-file-control:focus:before, .focus.custom-file-control:before { - outline: 0; - box-shadow: none -} - -.btn.disabled, .btn:disabled, .custom-file-control:disabled:before, .disabled.custom-file-control:before { - opacity: .65; - box-shadow: none -} - -.btn:not(:disabled):not(.disabled), .custom-file-control:not(:disabled):not(.disabled):before { - cursor: pointer -} - -.btn:not(:disabled):not(.disabled).active, .btn:not(:disabled):not(.disabled):active, .custom-file-control:not(:disabled):not(.disabled).active:before, .custom-file-control:not(:disabled):not(.disabled):active:before { - background-image: none; - box-shadow: none -} - -.btn:not(:disabled):not(.disabled).active:focus, .btn:not(:disabled):not(.disabled):active:focus, .custom-file-control:not(:disabled):not(.disabled).active:focus:before, .custom-file-control:not(:disabled):not(.disabled):active:focus:before { - box-shadow: none, none -} - -a.btn.disabled, a.disabled.custom-file-control:before, fieldset:disabled a.btn, fieldset:disabled a.custom-file-control:before { - pointer-events: none -} - -.btn-primary { - color: #fff; - background-color: #009688; - border-color: #009688; - box-shadow: none -} - -.btn-primary:hover { - color: #fff; - background-color: #007065; - border-color: #00635a -} - -.btn-primary.focus, .btn-primary:focus { - box-shadow: none, 0 0 0 .2rem rgba(0, 150, 136, .5) -} - -.btn-primary.disabled, .btn-primary:disabled { - color: #fff; - background-color: #009688; - border-color: #009688 -} - -.btn-primary:not(:disabled):not(.disabled).active, .btn-primary:not(:disabled):not(.disabled):active, .show > .btn-primary.dropdown-toggle { - color: #fff; - background-color: #00635a; - border-color: #00564e -} - -.btn-primary:not(:disabled):not(.disabled).active:focus, .btn-primary:not(:disabled):not(.disabled):active:focus, .show > .btn-primary.dropdown-toggle:focus { - box-shadow: none, 0 0 0 .2rem rgba(0, 150, 136, .5) -} - -.btn-secondary { - color: #fff; - background-color: #6c757d; - border-color: #6c757d; - box-shadow: none -} - -.btn-secondary:hover { - color: #fff; - background-color: #5a6268; - border-color: #545b62 -} - -.btn-secondary.focus, .btn-secondary:focus { - box-shadow: none, 0 0 0 .2rem hsla(208, 7%, 46%, .5) -} - -.btn-secondary.disabled, .btn-secondary:disabled { - color: #fff; - background-color: #6c757d; - border-color: #6c757d -} - -.btn-secondary:not(:disabled):not(.disabled).active, .btn-secondary:not(:disabled):not(.disabled):active, .show > .btn-secondary.dropdown-toggle { - color: #fff; - background-color: #545b62; - border-color: #4e555b -} - -.btn-secondary:not(:disabled):not(.disabled).active:focus, .btn-secondary:not(:disabled):not(.disabled):active:focus, .show > .btn-secondary.dropdown-toggle:focus { - box-shadow: none, 0 0 0 .2rem hsla(208, 7%, 46%, .5) -} - -.btn-success { - color: #fff; - background-color: #4caf50; - border-color: #4caf50; - box-shadow: none -} - -.btn-success:hover { - color: #fff; - background-color: #409444; - border-color: #3d8b40 -} - -.btn-success.focus, .btn-success:focus { - box-shadow: none, 0 0 0 .2rem rgba(76, 175, 80, .5) -} - -.btn-success.disabled, .btn-success:disabled { - color: #fff; - background-color: #4caf50; - border-color: #4caf50 -} - -.btn-success:not(:disabled):not(.disabled).active, .btn-success:not(:disabled):not(.disabled):active, .show > .btn-success.dropdown-toggle { - color: #fff; - background-color: #3d8b40; - border-color: #39833c -} - -.btn-success:not(:disabled):not(.disabled).active:focus, .btn-success:not(:disabled):not(.disabled):active:focus, .show > .btn-success.dropdown-toggle:focus { - box-shadow: none, 0 0 0 .2rem rgba(76, 175, 80, .5) -} - -.btn-info { - color: #fff; - background-color: #03a9f4; - border-color: #03a9f4; - box-shadow: none -} - -.btn-info:hover { - color: #fff; - background-color: #038fce; - border-color: #0286c2 -} - -.btn-info.focus, .btn-info:focus { - box-shadow: none, 0 0 0 .2rem rgba(3, 169, 244, .5) -} - -.btn-info.disabled, .btn-info:disabled { - color: #fff; - background-color: #03a9f4; - border-color: #03a9f4 -} - -.btn-info:not(:disabled):not(.disabled).active, .btn-info:not(:disabled):not(.disabled):active, .show > .btn-info.dropdown-toggle { - color: #fff; - background-color: #0286c2; - border-color: #027db5 -} - -.btn-info:not(:disabled):not(.disabled).active:focus, .btn-info:not(:disabled):not(.disabled):active:focus, .show > .btn-info.dropdown-toggle:focus { - box-shadow: none, 0 0 0 .2rem rgba(3, 169, 244, .5) -} - -.btn-warning { - color: #fff; - background-color: #ff5722; - border-color: #ff5722; - box-shadow: none -} - -.btn-warning:hover { - color: #fff; - background-color: #fb3c00; - border-color: #ee3900 -} - -.btn-warning.focus, .btn-warning:focus { - box-shadow: none, 0 0 0 .2rem rgba(255, 87, 34, .5) -} - -.btn-warning.disabled, .btn-warning:disabled { - color: #fff; - background-color: #ff5722; - border-color: #ff5722 -} - -.btn-warning:not(:disabled):not(.disabled).active, .btn-warning:not(:disabled):not(.disabled):active, .show > .btn-warning.dropdown-toggle { - color: #fff; - background-color: #ee3900; - border-color: #e13600 -} - -.btn-warning:not(:disabled):not(.disabled).active:focus, .btn-warning:not(:disabled):not(.disabled):active:focus, .show > .btn-warning.dropdown-toggle:focus { - box-shadow: none, 0 0 0 .2rem rgba(255, 87, 34, .5) -} - -.btn-danger { - color: #fff; - background-color: #f44336; - border-color: #f44336; - box-shadow: none -} - -.btn-danger:hover { - color: #fff; - background-color: #f22112; - border-color: #ea1c0d -} - -.btn-danger.focus, .btn-danger:focus { - box-shadow: none, 0 0 0 .2rem rgba(244, 67, 54, .5) -} - -.btn-danger.disabled, .btn-danger:disabled { - color: #fff; - background-color: #f44336; - border-color: #f44336 -} - -.btn-danger:not(:disabled):not(.disabled).active, .btn-danger:not(:disabled):not(.disabled):active, .show > .btn-danger.dropdown-toggle { - color: #fff; - background-color: #ea1c0d; - border-color: #de1b0c -} - -.btn-danger:not(:disabled):not(.disabled).active:focus, .btn-danger:not(:disabled):not(.disabled):active:focus, .show > .btn-danger.dropdown-toggle:focus { - box-shadow: none, 0 0 0 .2rem rgba(244, 67, 54, .5) -} - -.btn-light { - color: #212529; - background-color: #f5f5f5; - border-color: #f5f5f5; - box-shadow: none -} - -.btn-light:hover { - color: #212529; - background-color: #e2e2e2; - border-color: #dcdcdc -} - -.btn-light.focus, .btn-light:focus { - box-shadow: none, 0 0 0 .2rem hsla(0, 0%, 96%, .5) -} - -.btn-light.disabled, .btn-light:disabled { - color: #212529; - background-color: #f5f5f5; - border-color: #f5f5f5 -} - -.btn-light:not(:disabled):not(.disabled).active, .btn-light:not(:disabled):not(.disabled):active, .show > .btn-light.dropdown-toggle { - color: #212529; - background-color: #dcdcdc; - border-color: #d5d5d5 -} - -.btn-light:not(:disabled):not(.disabled).active:focus, .btn-light:not(:disabled):not(.disabled):active:focus, .show > .btn-light.dropdown-toggle:focus { - box-shadow: none, 0 0 0 .2rem hsla(0, 0%, 96%, .5) -} - -.btn-dark { - color: #fff; - background-color: #424242; - border-color: #424242; - box-shadow: none -} - -.btn-dark:hover { - color: #fff; - background-color: #2f2f2f; - border-color: #292929 -} - -.btn-dark.focus, .btn-dark:focus { - box-shadow: none, 0 0 0 .2rem rgba(66, 66, 66, .5) -} - -.btn-dark.disabled, .btn-dark:disabled { - color: #fff; - background-color: #424242; - border-color: #424242 -} - -.btn-dark:not(:disabled):not(.disabled).active, .btn-dark:not(:disabled):not(.disabled):active, .show > .btn-dark.dropdown-toggle { - color: #fff; - background-color: #292929; - border-color: #222 -} - -.btn-dark:not(:disabled):not(.disabled).active:focus, .btn-dark:not(:disabled):not(.disabled):active:focus, .show > .btn-dark.dropdown-toggle:focus { - box-shadow: none, 0 0 0 .2rem rgba(66, 66, 66, .5) -} - -.btn-outline-primary { - color: #009688; - background-color: transparent; - background-image: none; - border-color: #009688 -} - -.btn-outline-primary:hover { - color: #fff; - background-color: #009688; - border-color: #009688 -} - -.btn-outline-primary.focus, .btn-outline-primary:focus { - box-shadow: 0 0 0 .2rem rgba(0, 150, 136, .5) -} - -.btn-outline-primary.disabled, .btn-outline-primary:disabled { - color: #009688; - background-color: transparent -} - -.btn-outline-primary:not(:disabled):not(.disabled).active, .btn-outline-primary:not(:disabled):not(.disabled):active, .show > .btn-outline-primary.dropdown-toggle { - color: #fff; - background-color: #009688; - border-color: #009688 -} - -.btn-outline-primary:not(:disabled):not(.disabled).active:focus, .btn-outline-primary:not(:disabled):not(.disabled):active:focus, .show > .btn-outline-primary.dropdown-toggle:focus { - box-shadow: 0 0 0 .2rem rgba(0, 150, 136, .5) -} - -.btn-outline-secondary { - color: #6c757d; - background-color: transparent; - background-image: none; - border-color: #6c757d -} - -.btn-outline-secondary:hover { - color: #fff; - background-color: #6c757d; - border-color: #6c757d -} - -.btn-outline-secondary.focus, .btn-outline-secondary:focus { - box-shadow: 0 0 0 .2rem hsla(208, 7%, 46%, .5) -} - -.btn-outline-secondary.disabled, .btn-outline-secondary:disabled { - color: #6c757d; - background-color: transparent -} - -.btn-outline-secondary:not(:disabled):not(.disabled).active, .btn-outline-secondary:not(:disabled):not(.disabled):active, .show > .btn-outline-secondary.dropdown-toggle { - color: #fff; - background-color: #6c757d; - border-color: #6c757d -} - -.btn-outline-secondary:not(:disabled):not(.disabled).active:focus, .btn-outline-secondary:not(:disabled):not(.disabled):active:focus, .show > .btn-outline-secondary.dropdown-toggle:focus { - box-shadow: 0 0 0 .2rem hsla(208, 7%, 46%, .5) -} - -.btn-outline-success { - color: #4caf50; - background-color: transparent; - background-image: none; - border-color: #4caf50 -} - -.btn-outline-success:hover { - color: #fff; - background-color: #4caf50; - border-color: #4caf50 -} - -.btn-outline-success.focus, .btn-outline-success:focus { - box-shadow: 0 0 0 .2rem rgba(76, 175, 80, .5) -} - -.btn-outline-success.disabled, .btn-outline-success:disabled { - color: #4caf50; - background-color: transparent -} - -.btn-outline-success:not(:disabled):not(.disabled).active, .btn-outline-success:not(:disabled):not(.disabled):active, .show > .btn-outline-success.dropdown-toggle { - color: #fff; - background-color: #4caf50; - border-color: #4caf50 -} - -.btn-outline-success:not(:disabled):not(.disabled).active:focus, .btn-outline-success:not(:disabled):not(.disabled):active:focus, .show > .btn-outline-success.dropdown-toggle:focus { - box-shadow: 0 0 0 .2rem rgba(76, 175, 80, .5) -} - -.btn-outline-info { - color: #03a9f4; - background-color: transparent; - background-image: none; - border-color: #03a9f4 -} - -.btn-outline-info:hover { - color: #fff; - background-color: #03a9f4; - border-color: #03a9f4 -} - -.btn-outline-info.focus, .btn-outline-info:focus { - box-shadow: 0 0 0 .2rem rgba(3, 169, 244, .5) -} - -.btn-outline-info.disabled, .btn-outline-info:disabled { - color: #03a9f4; - background-color: transparent -} - -.btn-outline-info:not(:disabled):not(.disabled).active, .btn-outline-info:not(:disabled):not(.disabled):active, .show > .btn-outline-info.dropdown-toggle { - color: #fff; - background-color: #03a9f4; - border-color: #03a9f4 -} - -.btn-outline-info:not(:disabled):not(.disabled).active:focus, .btn-outline-info:not(:disabled):not(.disabled):active:focus, .show > .btn-outline-info.dropdown-toggle:focus { - box-shadow: 0 0 0 .2rem rgba(3, 169, 244, .5) -} - -.btn-outline-warning { - color: #ff5722; - background-color: transparent; - background-image: none; - border-color: #ff5722 -} - -.btn-outline-warning:hover { - color: #fff; - background-color: #ff5722; - border-color: #ff5722 -} - -.btn-outline-warning.focus, .btn-outline-warning:focus { - box-shadow: 0 0 0 .2rem rgba(255, 87, 34, .5) -} - -.btn-outline-warning.disabled, .btn-outline-warning:disabled { - color: #ff5722; - background-color: transparent -} - -.btn-outline-warning:not(:disabled):not(.disabled).active, .btn-outline-warning:not(:disabled):not(.disabled):active, .show > .btn-outline-warning.dropdown-toggle { - color: #fff; - background-color: #ff5722; - border-color: #ff5722 -} - -.btn-outline-warning:not(:disabled):not(.disabled).active:focus, .btn-outline-warning:not(:disabled):not(.disabled):active:focus, .show > .btn-outline-warning.dropdown-toggle:focus { - box-shadow: 0 0 0 .2rem rgba(255, 87, 34, .5) -} - -.btn-outline-danger { - color: #f44336; - background-color: transparent; - background-image: none; - border-color: #f44336 -} - -.btn-outline-danger:hover { - color: #fff; - background-color: #f44336; - border-color: #f44336 -} - -.btn-outline-danger.focus, .btn-outline-danger:focus { - box-shadow: 0 0 0 .2rem rgba(244, 67, 54, .5) -} - -.btn-outline-danger.disabled, .btn-outline-danger:disabled { - color: #f44336; - background-color: transparent -} - -.btn-outline-danger:not(:disabled):not(.disabled).active, .btn-outline-danger:not(:disabled):not(.disabled):active, .show > .btn-outline-danger.dropdown-toggle { - color: #fff; - background-color: #f44336; - border-color: #f44336 -} - -.btn-outline-danger:not(:disabled):not(.disabled).active:focus, .btn-outline-danger:not(:disabled):not(.disabled):active:focus, .show > .btn-outline-danger.dropdown-toggle:focus { - box-shadow: 0 0 0 .2rem rgba(244, 67, 54, .5) -} - -.btn-outline-light { - color: #f5f5f5; - background-color: transparent; - background-image: none; - border-color: #f5f5f5 -} - -.btn-outline-light:hover { - color: #212529; - background-color: #f5f5f5; - border-color: #f5f5f5 -} - -.btn-outline-light.focus, .btn-outline-light:focus { - box-shadow: 0 0 0 .2rem hsla(0, 0%, 96%, .5) -} - -.btn-outline-light.disabled, .btn-outline-light:disabled { - color: #f5f5f5; - background-color: transparent -} - -.btn-outline-light:not(:disabled):not(.disabled).active, .btn-outline-light:not(:disabled):not(.disabled):active, .show > .btn-outline-light.dropdown-toggle { - color: #212529; - background-color: #f5f5f5; - border-color: #f5f5f5 -} - -.btn-outline-light:not(:disabled):not(.disabled).active:focus, .btn-outline-light:not(:disabled):not(.disabled):active:focus, .show > .btn-outline-light.dropdown-toggle:focus { - box-shadow: 0 0 0 .2rem hsla(0, 0%, 96%, .5) -} - -.btn-outline-dark { - color: #424242; - background-color: transparent; - background-image: none; - border-color: #424242 -} - -.btn-outline-dark:hover { - color: #fff; - background-color: #424242; - border-color: #424242 -} - -.btn-outline-dark.focus, .btn-outline-dark:focus { - box-shadow: 0 0 0 .2rem rgba(66, 66, 66, .5) -} - -.btn-outline-dark.disabled, .btn-outline-dark:disabled { - color: #424242; - background-color: transparent -} - -.btn-outline-dark:not(:disabled):not(.disabled).active, .btn-outline-dark:not(:disabled):not(.disabled):active, .show > .btn-outline-dark.dropdown-toggle { - color: #fff; - background-color: #424242; - border-color: #424242 -} - -.btn-outline-dark:not(:disabled):not(.disabled).active:focus, .btn-outline-dark:not(:disabled):not(.disabled):active:focus, .show > .btn-outline-dark.dropdown-toggle:focus { - box-shadow: 0 0 0 .2rem rgba(66, 66, 66, .5) -} - -.btn-link { - font-weight: 400; - color: #009688; - background-color: transparent -} - -.btn-link:hover { - color: #004a43; - background-color: transparent -} - -.btn-link.focus, .btn-link:focus, .btn-link:hover { - text-decoration: underline; - border-color: transparent -} - -.btn-link.focus, .btn-link:focus { - box-shadow: none -} - -.btn-link.disabled, .btn-link:disabled { - color: rgba(0, 0, 0, .26) -} - -.btn-group-lg > .btn, .btn-group-lg > .custom-file-control:before, .btn-lg { - padding: .5rem 1rem; - font-size: 1.25rem; - line-height: 1.5; - border-radius: .3rem -} - -.btn-group-sm > .btn, .btn-group-sm > .custom-file-control:before, .btn-sm { - padding: .40625rem .5rem; - font-size: .875rem; - line-height: 1.5; - border-radius: .0625rem -} - -.btn-block { - display: block; - width: 100% -} - -.btn-block + .btn-block { - margin-top: .5rem -} - -input[type=button].btn-block, input[type=reset].btn-block, input[type=submit].btn-block { - width: 100% -} - -.fade { - opacity: 0; - transition: opacity .15s linear -} - -.fade.show { - opacity: 1 -} - -.collapse { - display: none -} - -.collapse.show { - display: block -} - -tr.collapse.show { - display: table-row -} - -tbody.collapse.show { - display: table-row-group -} - -.collapsing { - height: 0; - overflow: hidden; - transition: height .35s ease -} - -.collapsing, .dropdown, .dropup { - position: relative -} - -.dropdown-toggle:after { - display: inline-block; - width: 0; - height: 0; - margin-left: .255em; - vertical-align: .255em; - content: ""; - border-top: .3em solid; - border-right: .3em solid transparent; - border-bottom: 0; - border-left: .3em solid transparent -} - -.dropdown-toggle:empty:after { - margin-left: 0 -} - -.dropdown-menu { - position: absolute; - top: 100%; - left: 0; - z-index: 1000; - float: left; - min-width: 10rem; - padding: .5rem 0; - margin: .125rem 0 0; - font-size: 1rem; - color: #212529; - text-align: left; - list-style: none; - background-color: #fff; - background-clip: padding-box; - border: 1px solid rgba(0, 0, 0, .15); - border-radius: .125rem; - box-shadow: 0 2px 2px 0 rgba(0, 0, 0, .14), 0 3px 1px -2px rgba(0, 0, 0, .2), 0 1px 5px 0 rgba(0, 0, 0, .12) -} - -.dropup .dropdown-menu { - margin-top: 0; - margin-bottom: .125rem -} - -.dropup .dropdown-toggle:after { - display: inline-block; - width: 0; - height: 0; - margin-left: .255em; - vertical-align: .255em; - content: ""; - border-top: 0; - border-right: .3em solid transparent; - border-bottom: .3em solid; - border-left: .3em solid transparent -} - -.dropup .dropdown-toggle:empty:after { - margin-left: 0 -} - -.dropright .dropdown-menu { - margin-top: 0; - margin-left: .125rem -} - -.dropright .dropdown-toggle:after { - display: inline-block; - width: 0; - height: 0; - margin-left: .255em; - vertical-align: .255em; - content: ""; - border-top: .3em solid transparent; - border-bottom: .3em solid transparent; - border-left: .3em solid -} - -.dropright .dropdown-toggle:empty:after { - margin-left: 0 -} - -.dropright .dropdown-toggle:after { - vertical-align: 0 -} - -.dropleft .dropdown-menu { - margin-top: 0; - margin-right: .125rem -} - -.dropleft .dropdown-toggle:after { - display: inline-block; - width: 0; - height: 0; - margin-left: .255em; - vertical-align: .255em; - content: ""; - display: none -} - -.dropleft .dropdown-toggle:before { - display: inline-block; - width: 0; - height: 0; - margin-right: .255em; - vertical-align: .255em; - content: ""; - border-top: .3em solid transparent; - border-right: .3em solid; - border-bottom: .3em solid transparent -} - -.dropleft .dropdown-toggle:empty:after { - margin-left: 0 -} - -.dropleft .dropdown-toggle:before { - vertical-align: 0 -} - -.dropdown-divider { - height: 0; - margin: .5rem 0; - overflow: hidden; - border-top: 1px solid #e9ecef -} - -.dropdown-item { - display: block; - width: 100%; - padding: .25rem 1.5rem; - clear: both; - font-weight: 400; - color: #212529; - text-align: inherit; - white-space: nowrap; - background-color: transparent; - border: 0 -} - -.dropdown-item:focus, .dropdown-item:hover { - color: #16181b; - text-decoration: none; - background-color: #f8f9fa -} - -.dropdown-item.active, .dropdown-item:active { - color: #fff; - text-decoration: none; - background-color: #009688 -} - -.dropdown-item.disabled, .dropdown-item:disabled { - color: #6c757d; - background-color: transparent -} - -.dropdown-menu.show { - display: block -} - -.dropdown-header { - display: block; - padding: .5rem 1.5rem; - margin-bottom: 0; - font-size: .875rem; - color: #6c757d; - white-space: nowrap -} - -.btn-group, .btn-group-vertical { - display: inline-flex; - vertical-align: middle -} - -.btn-group-vertical > .btn, .btn-group-vertical > .custom-file-control:before, .btn-group > .btn, .btn-group > .custom-file-control:before { - position: relative; - flex: 0 1 auto -} - -.btn-group-vertical > .active.custom-file-control:before, .btn-group-vertical > .btn.active, .btn-group-vertical > .btn:active, .btn-group-vertical > .btn:focus, .btn-group-vertical > .btn:hover, .btn-group-vertical > .custom-file-control:active:before, .btn-group-vertical > .custom-file-control:focus:before, .btn-group-vertical > .custom-file-control:hover:before, .btn-group > .active.custom-file-control:before, .btn-group > .btn.active, .btn-group > .btn:active, .btn-group > .btn:focus, .btn-group > .btn:hover, .btn-group > .custom-file-control:active:before, .btn-group > .custom-file-control:focus:before, .btn-group > .custom-file-control:hover:before { - z-index: 1 -} - -.btn-group-vertical .btn + .btn, .btn-group-vertical .btn + .btn-group, .btn-group-vertical .btn + .custom-file-control:before, .btn-group-vertical .btn-group + .btn, .btn-group-vertical .btn-group + .btn-group, .btn-group-vertical .btn-group + .custom-file-control:before, .btn-group-vertical .custom-file-control:before + .btn, .btn-group-vertical .custom-file-control:before + .btn-group, .btn-group-vertical .custom-file-control:before + .custom-file-control:before, .btn-group .btn + .btn, .btn-group .btn + .btn-group, .btn-group .btn + .custom-file-control:before, .btn-group .btn-group + .btn, .btn-group .btn-group + .btn-group, .btn-group .btn-group + .custom-file-control:before, .btn-group .custom-file-control:before + .btn, .btn-group .custom-file-control:before + .btn-group, .btn-group .custom-file-control:before + .custom-file-control:before { - margin-left: -1px -} - -.btn-toolbar { - display: flex; - flex-wrap: wrap; - justify-content: flex-start -} - -.btn-toolbar .input-group { - width: auto -} - -.btn-group > .btn:first-child, .btn-group > .custom-file-control:first-child:before { - margin-left: 0 -} - -.btn-group > .btn-group:not(:last-child) > .btn, .btn-group > .btn-group:not(:last-child) > .custom-file-control:before, .btn-group > .btn:not(:last-child):not(.dropdown-toggle), .btn-group > .custom-file-control:not(:last-child):not(.dropdown-toggle):before { - border-top-right-radius: 0; - border-bottom-right-radius: 0 -} - -.btn-group > .btn-group:not(:first-child) > .btn, .btn-group > .btn-group:not(:first-child) > .custom-file-control:before, .btn-group > .btn:not(:first-child), .btn-group > .custom-file-control:not(:first-child):before { - border-top-left-radius: 0; - border-bottom-left-radius: 0 -} - -.dropdown-toggle-split { - padding-right: .75rem; - padding-left: .75rem -} - -.dropdown-toggle-split:after { - margin-left: 0 -} - -.btn-group-sm > .btn + .dropdown-toggle-split, .btn-group-sm > .custom-file-control:before + .dropdown-toggle-split, .btn-sm + .dropdown-toggle-split { - padding-right: .375rem; - padding-left: .375rem -} - -.btn-group-lg > .btn + .dropdown-toggle-split, .btn-group-lg > .custom-file-control:before + .dropdown-toggle-split, .btn-lg + .dropdown-toggle-split { - padding-right: .75rem; - padding-left: .75rem -} - -.btn-group.show .dropdown-toggle, .btn-group.show .dropdown-toggle.btn-link { - box-shadow: none -} - -.btn-group-vertical { - flex-direction: column; - align-items: flex-start; - justify-content: center -} - -.btn-group-vertical .btn, .btn-group-vertical .btn-group, .btn-group-vertical .custom-file-control:before { - width: 100% -} - -.btn-group-vertical > .btn + .btn, .btn-group-vertical > .btn + .btn-group, .btn-group-vertical > .btn + .custom-file-control:before, .btn-group-vertical > .btn-group + .btn, .btn-group-vertical > .btn-group + .btn-group, .btn-group-vertical > .btn-group + .custom-file-control:before, .btn-group-vertical > .custom-file-control:before + .btn, .btn-group-vertical > .custom-file-control:before + .btn-group, .btn-group-vertical > .custom-file-control:before + .custom-file-control:before { - margin-top: -1px; - margin-left: 0 -} - -.btn-group-vertical > .btn-group:not(:last-child) > .btn, .btn-group-vertical > .btn-group:not(:last-child) > .custom-file-control:before, .btn-group-vertical > .btn:not(:last-child):not(.dropdown-toggle), .btn-group-vertical > .custom-file-control:not(:last-child):not(.dropdown-toggle):before { - border-bottom-right-radius: 0; - border-bottom-left-radius: 0 -} - -.btn-group-vertical > .btn-group:not(:first-child) > .btn, .btn-group-vertical > .btn-group:not(:first-child) > .custom-file-control:before, .btn-group-vertical > .btn:not(:first-child), .btn-group-vertical > .custom-file-control:not(:first-child):before { - border-top-left-radius: 0; - border-top-right-radius: 0 -} - -.btn-group-toggle > .btn, .btn-group-toggle > .btn-group > .btn, .btn-group-toggle > .btn-group > .custom-file-control:before, .btn-group-toggle > .custom-file-control:before { - margin-bottom: 0 -} - -.btn-group-toggle > .btn-group > .btn input[type=checkbox], .btn-group-toggle > .btn-group > .btn input[type=radio], .btn-group-toggle > .btn-group > .custom-file-control:before input[type=checkbox], .btn-group-toggle > .btn-group > .custom-file-control:before input[type=radio], .btn-group-toggle > .btn input[type=checkbox], .btn-group-toggle > .btn input[type=radio], .btn-group-toggle > .custom-file-control:before input[type=checkbox], .btn-group-toggle > .custom-file-control:before input[type=radio] { - position: absolute; - clip: rect(0, 0, 0, 0); - pointer-events: none -} - -.input-group { - position: relative; - display: flex; - flex-wrap: wrap; - align-items: stretch; - width: 100% -} - -.input-group > .custom-file, .input-group > .custom-file-control, .input-group > .custom-select, .input-group > .form-control { - position: relative; - flex: 1 1 auto; - width: 1%; - margin-bottom: 0 -} - -.input-group > .custom-file-control:focus, .input-group > .custom-file:focus, .input-group > .custom-select:focus, .input-group > .form-control:focus { - z-index: 3 -} - -.input-group > .custom-file + .custom-file, .input-group > .custom-file + .custom-file-control, .input-group > .custom-file + .custom-select, .input-group > .custom-file + .form-control, .input-group > .custom-file-control + .custom-file, .input-group > .custom-file-control + .custom-file-control, .input-group > .custom-file-control + .custom-select, .input-group > .custom-file-control + .form-control, .input-group > .custom-select + .custom-file, .input-group > .custom-select + .custom-file-control, .input-group > .custom-select + .custom-select, .input-group > .custom-select + .form-control, .input-group > .form-control + .custom-file, .input-group > .form-control + .custom-file-control, .input-group > .form-control + .custom-select, .input-group > .form-control + .form-control { - margin-left: -1px -} - -.input-group > .custom-file-control:not(:last-child), .input-group > .custom-select:not(:last-child), .input-group > .form-control:not(:last-child) { - border-top-right-radius: 0; - border-bottom-right-radius: 0 -} - -.input-group > .custom-file-control:not(:first-child), .input-group > .custom-select:not(:first-child), .input-group > .form-control:not(:first-child) { - border-top-left-radius: 0; - border-bottom-left-radius: 0 -} - -.input-group > .custom-file { - display: flex; - align-items: center -} - -.input-group > .custom-file:not(:last-child) .custom-file-label, .input-group > .custom-file:not(:last-child) .custom-file-label:before { - border-top-right-radius: 0; - border-bottom-right-radius: 0 -} - -.input-group > .custom-file:not(:first-child) .custom-file-label, .input-group > .custom-file:not(:first-child) .custom-file-label:before { - border-top-left-radius: 0; - border-bottom-left-radius: 0 -} - -.input-group-append, .input-group-prepend { - display: flex -} - -.input-group-append .btn, .input-group-append .custom-file-control:before, .input-group-prepend .btn, .input-group-prepend .custom-file-control:before { - position: relative; - z-index: 2 -} - -.input-group-append .btn + .btn, .input-group-append .btn + .custom-file-control:before, .input-group-append .btn + .input-group-text, .input-group-append .custom-file-control:before + .btn, .input-group-append .custom-file-control:before + .custom-file-control:before, .input-group-append .custom-file-control:before + .input-group-text, .input-group-append .input-group-text + .btn, .input-group-append .input-group-text + .custom-file-control:before, .input-group-append .input-group-text + .input-group-text, .input-group-prepend .btn + .btn, .input-group-prepend .btn + .custom-file-control:before, .input-group-prepend .btn + .input-group-text, .input-group-prepend .custom-file-control:before + .btn, .input-group-prepend .custom-file-control:before + .custom-file-control:before, .input-group-prepend .custom-file-control:before + .input-group-text, .input-group-prepend .input-group-text + .btn, .input-group-prepend .input-group-text + .custom-file-control:before, .input-group-prepend .input-group-text + .input-group-text { - margin-left: -1px -} - -.input-group-prepend { - margin-right: -1px -} - -.input-group-append { - margin-left: -1px -} - -.input-group-text { - display: flex; - align-items: center; - padding: .4375rem 0; - margin-bottom: 0; - font-size: 1rem; - font-weight: 400; - line-height: 1.5; - color: #495057; - text-align: center; - white-space: nowrap; - background-color: transparent; - border: 1px solid transparent; - border-radius: 0 -} - -.input-group-text input[type=checkbox], .input-group-text input[type=radio] { - margin-top: 0 -} - -.input-group > .input-group-append:last-child > .btn:not(:last-child):not(.dropdown-toggle), .input-group > .input-group-append:last-child > .custom-file-control:not(:last-child):not(.dropdown-toggle):before, .input-group > .input-group-append:last-child > .input-group-text:not(:last-child), .input-group > .input-group-append:not(:last-child) > .btn, .input-group > .input-group-append:not(:last-child) > .custom-file-control:before, .input-group > .input-group-append:not(:last-child) > .input-group-text, .input-group > .input-group-prepend > .btn, .input-group > .input-group-prepend > .custom-file-control:before, .input-group > .input-group-prepend > .input-group-text { - border-top-right-radius: 0; - border-bottom-right-radius: 0 -} - -.input-group > .input-group-append > .btn, .input-group > .input-group-append > .custom-file-control:before, .input-group > .input-group-append > .input-group-text, .input-group > .input-group-prepend:first-child > .btn:not(:first-child), .input-group > .input-group-prepend:first-child > .custom-file-control:not(:first-child):before, .input-group > .input-group-prepend:first-child > .input-group-text:not(:first-child), .input-group > .input-group-prepend:not(:first-child) > .btn, .input-group > .input-group-prepend:not(:first-child) > .custom-file-control:before, .input-group > .input-group-prepend:not(:first-child) > .input-group-text { - border-top-left-radius: 0; - border-bottom-left-radius: 0 -} - -.custom-control { - position: relative; - display: block; - min-height: 1.5rem; - padding-left: 1.5rem -} - -.custom-control-inline { - display: inline-flex; - margin-right: 1rem -} - -.custom-control-input { - position: absolute; - z-index: -1; - opacity: 0 -} - -.custom-control-input:checked ~ .custom-control-label:before { - color: #fff; - background-color: #009688; - box-shadow: none -} - -.custom-control-input:focus ~ .custom-control-label:before { - box-shadow: 0 0 0 1px #fafafa, 0 0 0 .2rem rgba(0, 150, 136, .25) -} - -.custom-control-input:active ~ .custom-control-label:before { - color: #fff; - background-color: #4affee; - box-shadow: none -} - -.custom-control-input:disabled ~ .custom-control-label { - color: #6c757d -} - -.custom-control-input:disabled ~ .custom-control-label:before { - background-color: #e9ecef -} - -.custom-control-label { - margin-bottom: 0 -} - -.custom-control-label:before { - pointer-events: none; - user-select: none; - background-color: #dee2e6; - box-shadow: inset 0 .25rem .25rem rgba(0, 0, 0, .1) -} - -.custom-control-label:after, .custom-control-label:before { - position: absolute; - top: .25rem; - left: 0; - display: block; - width: 1rem; - height: 1rem; - content: "" -} - -.custom-control-label:after { - background-repeat: no-repeat; - background-position: 50%; - background-size: 50% 50% -} - -.custom-checkbox .custom-control-label:before { - border-radius: .125rem -} - -.custom-checkbox .custom-control-input:checked ~ .custom-control-label:before { - background-color: #009688 -} - -.custom-checkbox .custom-control-input:checked ~ .custom-control-label:after { - background-image: url("data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3E%3Cpath fill='%23fff' d='M6.564.75l-3.59 3.612-1.538-1.55L0 4.26 2.974 7.25 8 2.193z'/%3E%3C/svg%3E") -} - -.custom-checkbox .custom-control-input:indeterminate ~ .custom-control-label:before { - background-color: #009688; - box-shadow: none -} - -.custom-checkbox .custom-control-input:indeterminate ~ .custom-control-label:after { - background-image: url("data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 4 4'%3E%3Cpath stroke='%23fff' d='M0 2h4'/%3E%3C/svg%3E") -} - -.custom-checkbox .custom-control-input:disabled:checked ~ .custom-control-label:before { - background-color: rgba(0, 150, 136, .5) -} - -.custom-checkbox .custom-control-input:disabled:indeterminate ~ .custom-control-label:before { - background-color: rgba(0, 150, 136, .5) -} - -.custom-radio .custom-control-label:before { - border-radius: 50% -} - -.custom-radio .custom-control-input:checked ~ .custom-control-label:before { - background-color: #009688 -} - -.custom-radio .custom-control-input:checked ~ .custom-control-label:after { - background-image: url("data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='-4 -4 8 8'%3E%3Ccircle r='3' fill='%23fff'/%3E%3C/svg%3E") -} - -.custom-radio .custom-control-input:disabled:checked ~ .custom-control-label:before { - background-color: rgba(0, 150, 136, .5) -} - -.custom-select { - display: inline-block; - width: 100%; - height: calc(2.4375rem + 2px); - padding: .375rem 1.75rem .375rem .75rem; - line-height: 1.5; - color: #495057; - vertical-align: middle; - background: #fff url("data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 4 5'%3E%3Cpath fill='%23343a40' d='M2 0L0 2h4zm0 5L0 3h4z'/%3E%3C/svg%3E") no-repeat right .75rem center; - background-size: 8px 10px; - border: 1px solid rgba(0, 0, 0, .26); - border-radius: .125rem; - appearance: none -} - -.custom-select:focus { - border-color: #17ffe9; - outline: 0; - box-shadow: inset 0 1px 2px rgba(0, 0, 0, .075), 0 0 5px rgba(23, 255, 233, .5) -} - -.custom-select:focus::-ms-value { - color: #495057; - background-color: transparent -} - -.custom-select[multiple], .custom-select[size]:not([size="1"]) { - height: auto; - padding-right: .75rem; - background-image: none -} - -.custom-select:disabled { - color: #6c757d; - background-color: #e9ecef -} - -.custom-select::-ms-expand { - opacity: 0 -} - -.custom-select-sm { - height: calc(2.125rem + 2px); - font-size: 75% -} - -.custom-select-lg, .custom-select-sm { - padding-top: .375rem; - padding-bottom: .375rem -} - -.custom-select-lg { - height: calc(2.875rem + 2px); - font-size: 125% -} - -.custom-file { - display: inline-block; - margin-bottom: 0 -} - -.custom-file, .custom-file-input { - position: relative; - width: 100%; - height: calc(2.4375rem + 2px) -} - -.custom-file-input { - z-index: 2; - margin: 0; - opacity: 0 -} - -.custom-file-input:focus ~ .custom-file-control { - border-color: #17ffe9; - box-shadow: 0 0 0 .2rem rgba(0, 150, 136, .25) -} - -.custom-file-input:focus ~ .custom-file-control:before { - border-color: #17ffe9 -} - -.custom-file-input:lang(en) ~ .custom-file-label:after { - content: "Browse" -} - -.custom-file-label { - left: 0; - z-index: 1; - height: calc(2.4375rem + 2px); - border: 0 solid rgba(0, 0, 0, .26); - border-radius: 0; - box-shadow: none -} - -.custom-file-label, .custom-file-label:after { - position: absolute; - top: 0; - right: 0; - padding: .46875rem 1rem; - line-height: 1.3; - color: #495057; - background-color: transparent -} - -.custom-file-label:after { - bottom: 0; - z-index: 3; - display: block; - height: calc((2.4375rem + 2px) - 0 * 2); - content: "Browse"; - border-left: 0 solid rgba(0, 0, 0, .26); - border-radius: 0 0 0 0 -} - -.nav { - display: flex; - flex-wrap: wrap; - padding-left: 0; - margin-bottom: 0; - list-style: none -} - -.nav-link { - display: block; - padding: .5rem 1rem -} - -.nav-link:focus, .nav-link:hover { - text-decoration: none -} - -.nav-link.disabled { - color: #6c757d -} - -.nav-tabs { - border-bottom: 1px solid #dee2e6 -} - -.nav-tabs .nav-item { - margin-bottom: -1px -} - -.nav-tabs .nav-link { - border: 1px solid transparent; - border-top-left-radius: .125rem; - border-top-right-radius: .125rem -} - -.nav-tabs .nav-link:focus, .nav-tabs .nav-link:hover { - border-color: #e9ecef #e9ecef #dee2e6 -} - -.nav-tabs .nav-link.disabled { - color: #6c757d; - background-color: transparent; - border-color: transparent -} - -.nav-tabs .nav-item.show .nav-link, .nav-tabs .nav-link.active { - color: #495057; - background-color: #fafafa; - border-color: #dee2e6 #dee2e6 #fafafa -} - -.nav-tabs .dropdown-menu { - margin-top: -1px; - border-top-left-radius: 0; - border-top-right-radius: 0 -} - -.nav-pills .nav-link { - border-radius: .125rem -} - -.nav-pills .nav-link.active, .nav-pills .show > .nav-link { - color: #fff; - background-color: #009688 -} - -.nav-fill .nav-item { - flex: 1 1 auto; - text-align: center -} - -.nav-justified .nav-item { - flex-basis: 0; - flex-grow: 1; - text-align: center -} - -.tab-content > .tab-pane { - display: none -} - -.tab-content > .active { - display: block -} - -.navbar { - position: relative; - padding: .5rem 1rem -} - -.navbar, .navbar > .container, .navbar > .container-fluid { - display: flex; - flex-wrap: wrap; - align-items: center; - justify-content: space-between -} - -.navbar-brand { - display: inline-block; - padding-top: .3125rem; - padding-bottom: .3125rem; - margin-right: 1rem; - font-size: 1.25rem; - line-height: inherit; - white-space: nowrap -} - -.navbar-brand:focus, .navbar-brand:hover { - text-decoration: none -} - -.navbar-nav { - display: flex; - flex-direction: column; - padding-left: 0; - margin-bottom: 0; - list-style: none -} - -.navbar-nav .nav-link { - padding-right: 0; - padding-left: 0 -} - -.navbar-nav .dropdown-menu { - position: static; - float: none -} - -.navbar-text { - display: inline-block; - padding-top: .5rem; - padding-bottom: .5rem -} - -.navbar-collapse { - flex-basis: 100%; - flex-grow: 1; - align-items: center -} - -.navbar-toggler { - padding: .25rem .75rem; - font-size: 1.25rem; - line-height: 1; - background-color: transparent; - border: 1px solid transparent; - border-radius: .125rem -} - -.navbar-toggler:focus, .navbar-toggler:hover { - text-decoration: none -} - -.navbar-toggler:not(:disabled):not(.disabled) { - cursor: pointer -} - -.navbar-toggler-icon { - display: inline-block; - width: 1.5em; - height: 1.5em; - vertical-align: middle; - content: ""; - background: no-repeat 50%; - background-size: 100% 100% -} - -@media (max-width: 575.98px) { - .navbar-expand-sm > .container, .navbar-expand-sm > .container-fluid { - padding-right: 0; - padding-left: 0 - } -} - -@media (min-width: 576px) { - .navbar-expand-sm { - flex-flow: row nowrap; - justify-content: flex-start - } - - .navbar-expand-sm .navbar-nav { - flex-direction: row - } - - .navbar-expand-sm .navbar-nav .dropdown-menu { - position: absolute - } - - .navbar-expand-sm .navbar-nav .dropdown-menu-right { - right: 0; - left: auto - } - - .navbar-expand-sm .navbar-nav .nav-link { - padding-right: .5rem; - padding-left: .5rem - } - - .navbar-expand-sm > .container, .navbar-expand-sm > .container-fluid { - flex-wrap: nowrap - } - - .navbar-expand-sm .navbar-collapse { - display: flex !important; - flex-basis: auto - } - - .navbar-expand-sm .navbar-toggler { - display: none - } - - .navbar-expand-sm .dropup .dropdown-menu { - top: auto; - bottom: 100% - } -} - -@media (max-width: 767.98px) { - .navbar-expand-md > .container, .navbar-expand-md > .container-fluid { - padding-right: 0; - padding-left: 0 - } -} - -@media (min-width: 768px) { - .navbar-expand-md { - flex-flow: row nowrap; - justify-content: flex-start - } - - .navbar-expand-md .navbar-nav { - flex-direction: row - } - - .navbar-expand-md .navbar-nav .dropdown-menu { - position: absolute - } - - .navbar-expand-md .navbar-nav .dropdown-menu-right { - right: 0; - left: auto - } - - .navbar-expand-md .navbar-nav .nav-link { - padding-right: .5rem; - padding-left: .5rem - } - - .navbar-expand-md > .container, .navbar-expand-md > .container-fluid { - flex-wrap: nowrap - } - - .navbar-expand-md .navbar-collapse { - display: flex !important; - flex-basis: auto - } - - .navbar-expand-md .navbar-toggler { - display: none - } - - .navbar-expand-md .dropup .dropdown-menu { - top: auto; - bottom: 100% - } -} - -@media (max-width: 991.98px) { - .navbar-expand-lg > .container, .navbar-expand-lg > .container-fluid { - padding-right: 0; - padding-left: 0 - } -} - -@media (min-width: 992px) { - .navbar-expand-lg { - flex-flow: row nowrap; - justify-content: flex-start - } - - .navbar-expand-lg .navbar-nav { - flex-direction: row - } - - .navbar-expand-lg .navbar-nav .dropdown-menu { - position: absolute - } - - .navbar-expand-lg .navbar-nav .dropdown-menu-right { - right: 0; - left: auto - } - - .navbar-expand-lg .navbar-nav .nav-link { - padding-right: .5rem; - padding-left: .5rem - } - - .navbar-expand-lg > .container, .navbar-expand-lg > .container-fluid { - flex-wrap: nowrap - } - - .navbar-expand-lg .navbar-collapse { - display: flex !important; - flex-basis: auto - } - - .navbar-expand-lg .navbar-toggler { - display: none - } - - .navbar-expand-lg .dropup .dropdown-menu { - top: auto; - bottom: 100% - } -} - -@media (max-width: 1199.98px) { - .navbar-expand-xl > .container, .navbar-expand-xl > .container-fluid { - padding-right: 0; - padding-left: 0 - } -} - -@media (min-width: 1200px) { - .navbar-expand-xl { - flex-flow: row nowrap; - justify-content: flex-start - } - - .navbar-expand-xl .navbar-nav { - flex-direction: row - } - - .navbar-expand-xl .navbar-nav .dropdown-menu { - position: absolute - } - - .navbar-expand-xl .navbar-nav .dropdown-menu-right { - right: 0; - left: auto - } - - .navbar-expand-xl .navbar-nav .nav-link { - padding-right: .5rem; - padding-left: .5rem - } - - .navbar-expand-xl > .container, .navbar-expand-xl > .container-fluid { - flex-wrap: nowrap - } - - .navbar-expand-xl .navbar-collapse { - display: flex !important; - flex-basis: auto - } - - .navbar-expand-xl .navbar-toggler { - display: none - } - - .navbar-expand-xl .dropup .dropdown-menu { - top: auto; - bottom: 100% - } -} - -.navbar-expand { - flex-flow: row nowrap; - justify-content: flex-start -} - -.navbar-expand > .container, .navbar-expand > .container-fluid { - padding-right: 0; - padding-left: 0 -} - -.navbar-expand .navbar-nav { - flex-direction: row -} - -.navbar-expand .navbar-nav .dropdown-menu { - position: absolute -} - -.navbar-expand .navbar-nav .dropdown-menu-right { - right: 0; - left: auto -} - -.navbar-expand .navbar-nav .nav-link { - padding-right: .5rem; - padding-left: .5rem -} - -.navbar-expand > .container, .navbar-expand > .container-fluid { - flex-wrap: nowrap -} - -.navbar-expand .navbar-collapse { - display: flex !important; - flex-basis: auto -} - -.navbar-expand .navbar-toggler { - display: none -} - -.navbar-expand .dropup .dropdown-menu { - top: auto; - bottom: 100% -} - -.navbar-light .navbar-brand, .navbar-light .navbar-brand:focus, .navbar-light .navbar-brand:hover { - color: rgba(0, 0, 0, .9) -} - -.navbar-light .navbar-nav .nav-link { - color: rgba(0, 0, 0, .5) -} - -.navbar-light .navbar-nav .nav-link:focus, .navbar-light .navbar-nav .nav-link:hover { - color: rgba(0, 0, 0, .7) -} - -.navbar-light .navbar-nav .nav-link.disabled { - color: rgba(0, 0, 0, .3) -} - -.navbar-light .navbar-nav .active > .nav-link, .navbar-light .navbar-nav .nav-link.active, .navbar-light .navbar-nav .nav-link.show, .navbar-light .navbar-nav .show > .nav-link { - color: rgba(0, 0, 0, .9) -} - -.navbar-light .navbar-toggler { - color: rgba(0, 0, 0, .5); - border-color: rgba(0, 0, 0, .1) -} - -.navbar-light .navbar-toggler-icon { - background-image: url("data:image/svg+xml;charset=utf8,%3Csvg viewBox='0 0 30 30' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath stroke='rgba(0, 0, 0, 0.5)' stroke-width='2' stroke-linecap='round' stroke-miterlimit='10' d='M4 7h22M4 15h22M4 23h22'/%3E%3C/svg%3E") -} - -.navbar-light .navbar-text { - color: rgba(0, 0, 0, .5) -} - -.navbar-light .navbar-text a, .navbar-light .navbar-text a:focus, .navbar-light .navbar-text a:hover { - color: rgba(0, 0, 0, .9) -} - -.navbar-dark .navbar-brand, .navbar-dark .navbar-brand:focus, .navbar-dark .navbar-brand:hover { - color: #fff -} - -.navbar-dark .navbar-nav .nav-link { - color: hsla(0, 0%, 100%, .5) -} - -.navbar-dark .navbar-nav .nav-link:focus, .navbar-dark .navbar-nav .nav-link:hover { - color: hsla(0, 0%, 100%, .75) -} - -.navbar-dark .navbar-nav .nav-link.disabled { - color: hsla(0, 0%, 100%, .25) -} - -.navbar-dark .navbar-nav .active > .nav-link, .navbar-dark .navbar-nav .nav-link.active, .navbar-dark .navbar-nav .nav-link.show, .navbar-dark .navbar-nav .show > .nav-link { - color: #fff -} - -.navbar-dark .navbar-toggler { - color: hsla(0, 0%, 100%, .5); - border-color: hsla(0, 0%, 100%, .1) -} - -.navbar-dark .navbar-toggler-icon { - background-image: url("data:image/svg+xml;charset=utf8,%3Csvg viewBox='0 0 30 30' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath stroke='rgba(255, 255, 255, 0.5)' stroke-width='2' stroke-linecap='round' stroke-miterlimit='10' d='M4 7h22M4 15h22M4 23h22'/%3E%3C/svg%3E") -} - -.navbar-dark .navbar-text { - color: hsla(0, 0%, 100%, .5) -} - -.navbar-dark .navbar-text a, .navbar-dark .navbar-text a:focus, .navbar-dark .navbar-text a:hover { - color: #fff -} - -.card { - position: relative; - display: flex; - flex-direction: column; - min-width: 0; - word-wrap: break-word; - background-color: #fff; - background-clip: border-box; - border: 1px solid rgba(0, 0, 0, .12); - border-radius: .125rem -} - -.card > hr { - margin-right: 0; - margin-left: 0 -} - -.card > .list-group:first-child .list-group-item:first-child { - border-top-left-radius: .125rem; - border-top-right-radius: .125rem -} - -.card > .list-group:last-child .list-group-item:last-child { - border-bottom-right-radius: .125rem; - border-bottom-left-radius: .125rem -} - -.card-body { - flex: 1 1 auto; - padding: 1.25rem -} - -.card-title { - margin-bottom: .75rem -} - -.card-subtitle { - margin-top: -.375rem -} - -.card-subtitle, .card-text:last-child { - margin-bottom: 0 -} - -.card-link:hover { - text-decoration: none -} - -.card-link + .card-link { - margin-left: 1.25rem -} - -.card-header { - padding: .75rem 1.25rem; - margin-bottom: 0; - background-color: #fff; - border-bottom: 1px solid rgba(0, 0, 0, .12) -} - -.card-header:first-child { - border-radius: calc(.125rem - 1px) calc(.125rem - 1px) 0 0 -} - -.card-header + .list-group .list-group-item:first-child { - border-top: 0 -} - -.card-footer { - padding: .75rem 1.25rem; - background-color: #fff; - border-top: 1px solid rgba(0, 0, 0, .12) -} - -.card-footer:last-child { - border-radius: 0 0 calc(.125rem - 1px) calc(.125rem - 1px) -} - -.card-header-tabs { - margin-bottom: -.75rem; - border-bottom: 0 -} - -.card-header-pills, .card-header-tabs { - margin-right: -.625rem; - margin-left: -.625rem -} - -.card-img-overlay { - position: absolute; - top: 0; - right: 0; - bottom: 0; - left: 0; - padding: 1.25rem -} - -.card-img { - width: 100%; - border-radius: calc(.125rem - 1px) -} - -.card-img-top { - width: 100%; - border-top-left-radius: calc(.125rem - 1px); - border-top-right-radius: calc(.125rem - 1px) -} - -.card-img-bottom { - width: 100%; - border-bottom-right-radius: calc(.125rem - 1px); - border-bottom-left-radius: calc(.125rem - 1px) -} - -.card-deck { - display: flex; - flex-direction: column -} - -.card-deck .card { - margin-bottom: 15px -} - -@media (min-width: 576px) { - .card-deck { - flex-flow: row wrap; - margin-right: -15px; - margin-left: -15px - } - - .card-deck .card { - display: flex; - flex: 1 0 0%; - flex-direction: column; - margin-right: 15px; - margin-bottom: 0; - margin-left: 15px - } -} - -.card-group { - display: flex; - flex-direction: column -} - -.card-group > .card { - margin-bottom: 15px -} - -@media (min-width: 576px) { - .card-group { - flex-flow: row wrap - } - - .card-group > .card { - flex: 1 0 0%; - margin-bottom: 0 - } - - .card-group > .card + .card { - margin-left: 0; - border-left: 0 - } - - .card-group > .card:first-child { - border-top-right-radius: 0; - border-bottom-right-radius: 0 - } - - .card-group > .card:first-child .card-header, .card-group > .card:first-child .card-img-top { - border-top-right-radius: 0 - } - - .card-group > .card:first-child .card-footer, .card-group > .card:first-child .card-img-bottom { - border-bottom-right-radius: 0 - } - - .card-group > .card:last-child { - border-top-left-radius: 0; - border-bottom-left-radius: 0 - } - - .card-group > .card:last-child .card-header, .card-group > .card:last-child .card-img-top { - border-top-left-radius: 0 - } - - .card-group > .card:last-child .card-footer, .card-group > .card:last-child .card-img-bottom { - border-bottom-left-radius: 0 - } - - .card-group > .card:only-child { - border-radius: .125rem - } - - .card-group > .card:only-child .card-header, .card-group > .card:only-child .card-img-top { - border-top-left-radius: .125rem; - border-top-right-radius: .125rem - } - - .card-group > .card:only-child .card-footer, .card-group > .card:only-child .card-img-bottom { - border-bottom-right-radius: .125rem; - border-bottom-left-radius: .125rem - } - - .card-group > .card:not(:first-child):not(:last-child):not(:only-child), .card-group > .card:not(:first-child):not(:last-child):not(:only-child) .card-footer, .card-group > .card:not(:first-child):not(:last-child):not(:only-child) .card-header, .card-group > .card:not(:first-child):not(:last-child):not(:only-child) .card-img-bottom, .card-group > .card:not(:first-child):not(:last-child):not(:only-child) .card-img-top { - border-radius: 0 - } -} - -.card-columns .card { - margin-bottom: .75rem -} - -@media (min-width: 576px) { - .card-columns { - column-count: 3; - column-gap: 1.25rem - } - - .card-columns .card { - display: inline-block; - width: 100% - } -} - -.breadcrumb { - display: flex; - flex-wrap: wrap; - padding: .75rem 1rem; - margin-bottom: 1rem; - list-style: none; - background-color: #e9ecef; - border-radius: .125rem -} - -.breadcrumb-item + .breadcrumb-item:before { - display: inline-block; - padding-right: .5rem; - padding-left: .5rem; - color: #6c757d; - content: "/" -} - -.breadcrumb-item + .breadcrumb-item:hover:before { - text-decoration: underline; - text-decoration: none -} - -.breadcrumb-item.active { - color: #6c757d -} - -.pagination { - display: flex; - padding-left: 0; - list-style: none; - border-radius: .125rem -} - -.page-link { - position: relative; - display: block; - padding: .5rem .75rem; - margin-left: 0; - line-height: 1.25; - color: #009688; - background-color: transparent; - border: 0 solid #dee2e6 -} - -.page-link:hover { - color: #004a43; - text-decoration: none; - background-color: #e9ecef; - border-color: #dee2e6 -} - -.page-link:focus { - z-index: 2; - outline: 0; - box-shadow: 0 0 0 .2rem rgba(0, 150, 136, .25) -} - -.page-link:not(:disabled):not(.disabled) { - cursor: pointer -} - -.page-item:first-child .page-link { - margin-left: 0; - border-top-left-radius: .125rem; - border-bottom-left-radius: .125rem -} - -.page-item:last-child .page-link { - border-top-right-radius: .125rem; - border-bottom-right-radius: .125rem -} - -.page-item.active .page-link { - z-index: 1; - color: #fff; - background-color: #009688; - border-color: #009688 -} - -.page-item.disabled .page-link { - color: #6c757d; - pointer-events: none; - cursor: auto; - background-color: transparent; - border-color: #dee2e6 -} - -.pagination-lg .page-item:first-child .page-link { - border-top-left-radius: .3rem; - border-bottom-left-radius: .3rem -} - -.pagination-lg .page-item:last-child .page-link { - border-top-right-radius: .3rem; - border-bottom-right-radius: .3rem -} - -.pagination-sm .page-item:first-child .page-link { - border-top-left-radius: .0625rem; - border-bottom-left-radius: .0625rem -} - -.pagination-sm .page-item:last-child .page-link { - border-top-right-radius: .0625rem; - border-bottom-right-radius: .0625rem -} - -.badge { - display: inline-block; - padding: .25em .4em; - font-size: 75%; - font-weight: 700; - line-height: 1; - text-align: center; - white-space: nowrap; - vertical-align: baseline; - border-radius: .125rem -} - -.badge:empty { - display: none -} - -.btn .badge, .custom-file-control:before .badge { - position: relative; - top: -1px -} - -.badge-pill { - padding-right: .6em; - padding-left: .6em; - border-radius: 10rem -} - -.badge-primary { - color: #fff; - background-color: #009688 -} - -.badge-primary[href]:focus, .badge-primary[href]:hover { - color: #fff; - text-decoration: none; - background-color: #00635a -} - -.badge-secondary { - color: #fff; - background-color: #6c757d -} - -.badge-secondary[href]:focus, .badge-secondary[href]:hover { - color: #fff; - text-decoration: none; - background-color: #545b62 -} - -.badge-success { - color: #fff; - background-color: #4caf50 -} - -.badge-success[href]:focus, .badge-success[href]:hover { - color: #fff; - text-decoration: none; - background-color: #3d8b40 -} - -.badge-info { - color: #fff; - background-color: #03a9f4 -} - -.badge-info[href]:focus, .badge-info[href]:hover { - color: #fff; - text-decoration: none; - background-color: #0286c2 -} - -.badge-warning { - color: #fff; - background-color: #ff5722 -} - -.badge-warning[href]:focus, .badge-warning[href]:hover { - color: #fff; - text-decoration: none; - background-color: #ee3900 -} - -.badge-danger { - color: #fff; - background-color: #f44336 -} - -.badge-danger[href]:focus, .badge-danger[href]:hover { - color: #fff; - text-decoration: none; - background-color: #ea1c0d -} - -.badge-light { - color: #212529; - background-color: #f5f5f5 -} - -.badge-light[href]:focus, .badge-light[href]:hover { - color: #212529; - text-decoration: none; - background-color: #dcdcdc -} - -.badge-dark { - color: #fff; - background-color: #424242 -} - -.badge-dark[href]:focus, .badge-dark[href]:hover { - color: #fff; - text-decoration: none; - background-color: #292929 -} - -.jumbotron { - padding: 2rem 1rem; - margin-bottom: 2rem; - background-color: #e9ecef; - border-radius: .3rem -} - -@media (min-width: 576px) { - .jumbotron { - padding: 4rem 2rem - } -} - -.jumbotron-fluid { - padding-right: 0; - padding-left: 0; - border-radius: 0 -} - -.alert { - position: relative; - padding: .75rem 1.25rem; - margin-bottom: 1rem; - border: 1px solid transparent; - border-radius: .125rem -} - -.alert-heading { - color: inherit -} - -.alert-link { - font-weight: 700 -} - -.alert-dismissible { - padding-right: 4rem -} - -.alert-dismissible .close { - position: absolute; - top: 0; - right: 0; - padding: .75rem 1.25rem; - color: inherit -} - -.alert-primary { - color: #004e47; - background-color: #cceae7; - border-color: #b8e2de -} - -.alert-primary hr { - border-top-color: #a6dbd6 -} - -.alert-primary .alert-link { - color: #001b19 -} - -.alert-secondary { - color: #383d41; - background-color: #e2e3e5; - border-color: #d6d8db -} - -.alert-secondary hr { - border-top-color: #c8cbcf -} - -.alert-secondary .alert-link { - color: #202326 -} - -.alert-success { - color: #285b2a; - background-color: #dbefdc; - border-color: #cde9ce -} - -.alert-success hr { - border-top-color: #bbe1bd -} - -.alert-success .alert-link { - color: #18381a -} - -.alert-info { - color: #02587f; - background-color: #cdeefd; - border-color: #b8e7fc -} - -.alert-info hr { - border-top-color: #a0dffb -} - -.alert-info .alert-link { - color: #01354d -} - -.alert-warning { - color: #852d12; - background-color: #ffddd3; - border-color: #ffd0c1 -} - -.alert-warning hr { - border-top-color: #ffbda8 -} - -.alert-warning .alert-link { - color: #581e0c -} - -.alert-danger { - color: #7f231c; - background-color: #fdd9d7; - border-color: #fccac7 -} - -.alert-danger hr { - border-top-color: #fbb3af -} - -.alert-danger .alert-link { - color: #551713 -} - -.alert-light { - color: #7f7f7f; - background-color: #fdfdfd; - border-color: #fcfcfc -} - -.alert-light hr { - border-top-color: #efefef -} - -.alert-light .alert-link { - color: #666 -} - -.alert-dark { - color: #222; - background-color: #d9d9d9; - border-color: #cacaca -} - -.alert-dark hr { - border-top-color: #bdbdbd -} - -.alert-dark .alert-link { - color: #090909 -} - -@keyframes a { - 0% { - background-position: 1rem 0 - } - to { - background-position: 0 0 - } -} - -.progress { - display: flex; - height: 1rem; - overflow: hidden; - font-size: .75rem; - background-color: #e9ecef; - border-radius: .125rem; - box-shadow: inset 0 .1rem .1rem rgba(0, 0, 0, .1) -} - -.progress-bar { - display: flex; - flex-direction: column; - justify-content: center; - color: #fff; - text-align: center; - background-color: #009688; - transition: width .6s ease -} - -.progress-bar-striped { - background-image: linear-gradient(45deg, hsla(0, 0%, 100%, .15) 25%, transparent 0, transparent 50%, hsla(0, 0%, 100%, .15) 0, hsla(0, 0%, 100%, .15) 75%, transparent 0, transparent); - background-size: 1rem 1rem -} - -.progress-bar-animated { - animation: a 1s linear infinite -} - -.media { - display: flex; - align-items: flex-start -} - -.media-body { - flex: 1 -} - -.list-group { - padding-left: 0; - margin-bottom: 0 -} - -.list-group-item-action { - width: 100%; - color: #495057; - text-align: inherit -} - -.list-group-item-action:focus, .list-group-item-action:hover { - color: #495057; - text-decoration: none; - background-color: #f8f9fa -} - -.list-group-item-action:active { - color: #212529; - background-color: #e9ecef -} - -.list-group-item { - position: relative; - display: block; - padding: .75rem 1.25rem; - margin-bottom: 0; - background-color: inherit; - border: 0 solid rgba(0, 0, 0, .125) -} - -.list-group-item:first-child { - border-top-left-radius: 0; - border-top-right-radius: 0 -} - -.list-group-item:last-child { - margin-bottom: 0; - border-bottom-right-radius: 0; - border-bottom-left-radius: 0 -} - -.list-group-item:focus, .list-group-item:hover { - z-index: 1; - text-decoration: none -} - -.list-group-item.disabled, .list-group-item:disabled { - color: #6c757d; - background-color: inherit -} - -.list-group-item.active { - z-index: 2; - color: #fff; - background-color: #009688; - border-color: #009688 -} - -.list-group-flush .list-group-item { - border-right: 0; - border-left: 0; - border-radius: 0 -} - -.list-group-flush:first-child .list-group-item:first-child { - border-top: 0 -} - -.list-group-flush:last-child .list-group-item:last-child { - border-bottom: 0 -} - -.list-group-item-primary { - color: #004e47; - background-color: #b8e2de -} - -.list-group-item-primary.list-group-item-action:focus, .list-group-item-primary.list-group-item-action:hover { - color: #004e47; - background-color: #a6dbd6 -} - -.list-group-item-primary.list-group-item-action.active { - color: #fff; - background-color: #004e47; - border-color: #004e47 -} - -.list-group-item-secondary { - color: #383d41; - background-color: #d6d8db -} - -.list-group-item-secondary.list-group-item-action:focus, .list-group-item-secondary.list-group-item-action:hover { - color: #383d41; - background-color: #c8cbcf -} - -.list-group-item-secondary.list-group-item-action.active { - color: #fff; - background-color: #383d41; - border-color: #383d41 -} - -.list-group-item-success { - color: #285b2a; - background-color: #cde9ce -} - -.list-group-item-success.list-group-item-action:focus, .list-group-item-success.list-group-item-action:hover { - color: #285b2a; - background-color: #bbe1bd -} - -.list-group-item-success.list-group-item-action.active { - color: #fff; - background-color: #285b2a; - border-color: #285b2a -} - -.list-group-item-info { - color: #02587f; - background-color: #b8e7fc -} - -.list-group-item-info.list-group-item-action:focus, .list-group-item-info.list-group-item-action:hover { - color: #02587f; - background-color: #a0dffb -} - -.list-group-item-info.list-group-item-action.active { - color: #fff; - background-color: #02587f; - border-color: #02587f -} - -.list-group-item-warning { - color: #852d12; - background-color: #ffd0c1 -} - -.list-group-item-warning.list-group-item-action:focus, .list-group-item-warning.list-group-item-action:hover { - color: #852d12; - background-color: #ffbda8 -} - -.list-group-item-warning.list-group-item-action.active { - color: #fff; - background-color: #852d12; - border-color: #852d12 -} - -.list-group-item-danger { - color: #7f231c; - background-color: #fccac7 -} - -.list-group-item-danger.list-group-item-action:focus, .list-group-item-danger.list-group-item-action:hover { - color: #7f231c; - background-color: #fbb3af -} - -.list-group-item-danger.list-group-item-action.active { - color: #fff; - background-color: #7f231c; - border-color: #7f231c -} - -.list-group-item-light { - color: #7f7f7f; - background-color: #fcfcfc -} - -.list-group-item-light.list-group-item-action:focus, .list-group-item-light.list-group-item-action:hover { - color: #7f7f7f; - background-color: #efefef -} - -.list-group-item-light.list-group-item-action.active { - color: #fff; - background-color: #7f7f7f; - border-color: #7f7f7f -} - -.list-group-item-dark { - color: #222; - background-color: #cacaca -} - -.list-group-item-dark.list-group-item-action:focus, .list-group-item-dark.list-group-item-action:hover { - color: #222; - background-color: #bdbdbd -} - -.list-group-item-dark.list-group-item-action.active { - color: #fff; - background-color: #222; - border-color: #222 -} - -.close { - float: right; - font-size: 1.5rem; - font-weight: 700; - line-height: 1; - color: #000; - text-shadow: 0 1px 0 #fff; - opacity: .5 -} - -.close:focus, .close:hover { - color: #000; - text-decoration: none; - opacity: .75 -} - -.close:not(:disabled):not(.disabled) { - cursor: pointer -} - -button.close { - padding: 0; - background-color: transparent; - border: 0; - -webkit-appearance: none -} - -.modal, .modal-open { - overflow: hidden -} - -.modal { - position: fixed; - top: 0; - right: 0; - bottom: 0; - left: 0; - z-index: 1050; - display: none; - outline: 0 -} - -.modal-open .modal { - overflow-x: hidden; - overflow-y: auto -} - -.modal-dialog { - position: relative; - width: auto; - margin: .5rem; - pointer-events: none -} - -.modal.fade .modal-dialog { - transition: transform .3s ease-out; - transform: translateY(-25%) -} - -.modal.show .modal-dialog { - transform: translate(0) -} - -.modal-dialog-centered { - display: flex; - align-items: center; - min-height: calc(100% - 1rem) -} - -.modal-content { - position: relative; - display: flex; - flex-direction: column; - width: 100%; - pointer-events: auto; - background-color: #fff; - background-clip: padding-box; - border: 1px solid rgba(0, 0, 0, .2); - border-radius: .3rem; - box-shadow: 0 .25rem .5rem rgba(0, 0, 0, .5); - outline: 0 -} - -.modal-backdrop { - position: fixed; - top: 0; - right: 0; - bottom: 0; - left: 0; - z-index: 1040; - background-color: #000 -} - -.modal-backdrop.fade { - opacity: 0 -} - -.modal-backdrop.show { - opacity: .26 -} - -.modal-header { - display: flex; - align-items: flex-start; - justify-content: space-between; - padding: 1rem; - border-bottom: 1px solid #e9ecef; - border-top-left-radius: .3rem; - border-top-right-radius: .3rem -} - -.modal-header .close { - padding: 1rem; - margin: -1rem -1rem -1rem auto -} - -.modal-title { - margin-bottom: 0; - line-height: 1.5 -} - -.modal-body { - position: relative; - flex: 1 1 auto; - padding: 1rem -} - -.modal-footer { - display: flex; - align-items: center; - justify-content: flex-end; - padding: 1rem; - border-top: 1px solid #e9ecef -} - -.modal-footer > :not(:first-child) { - margin-left: .25rem -} - -.modal-footer > :not(:last-child) { - margin-right: .25rem -} - -.modal-scrollbar-measure { - position: absolute; - top: -9999px; - width: 50px; - height: 50px; - overflow: scroll -} - -@media (min-width: 576px) { - .modal-dialog { - max-width: 500px; - margin: 1.75rem auto - } - - .modal-dialog-centered { - min-height: calc(100% - 3.5rem) - } - - .modal-content { - box-shadow: 0 .5rem 1rem rgba(0, 0, 0, .5) - } - - .modal-sm { - max-width: 300px - } -} - -@media (min-width: 992px) { - .modal-lg { - max-width: 800px - } -} - -.tooltip { - position: absolute; - z-index: 1070; - display: block; - margin: 0; - font-family: Roboto, Helvetica, Arial, sans-serif; - font-style: normal; - font-weight: 400; - line-height: 1.5; - text-align: left; - text-align: start; - text-decoration: none; - text-shadow: none; - text-transform: none; - letter-spacing: normal; - word-break: normal; - word-spacing: normal; - white-space: normal; - line-break: auto; - font-size: .875rem; - word-wrap: break-word; - opacity: 0 -} - -.tooltip.show { - opacity: .9 -} - -.tooltip .arrow { - position: absolute; - display: block; - width: .8rem; - height: .4rem -} - -.tooltip .arrow:before { - position: absolute; - content: ""; - border-color: transparent; - border-style: solid -} - -.bs-tooltip-auto[x-placement^=top], .bs-tooltip-top { - padding: .4rem 0 -} - -.bs-tooltip-auto[x-placement^=top] .arrow, .bs-tooltip-top .arrow { - bottom: 0 -} - -.bs-tooltip-auto[x-placement^=top] .arrow:before, .bs-tooltip-top .arrow:before { - top: 0; - border-width: .4rem .4rem 0; - border-top-color: rgba(97, 97, 97, .9) -} - -.bs-tooltip-auto[x-placement^=right], .bs-tooltip-right { - padding: 0 .4rem -} - -.bs-tooltip-auto[x-placement^=right] .arrow, .bs-tooltip-right .arrow { - left: 0; - width: .4rem; - height: .8rem -} - -.bs-tooltip-auto[x-placement^=right] .arrow:before, .bs-tooltip-right .arrow:before { - right: 0; - border-width: .4rem .4rem .4rem 0; - border-right-color: rgba(97, 97, 97, .9) -} - -.bs-tooltip-auto[x-placement^=bottom], .bs-tooltip-bottom { - padding: .4rem 0 -} - -.bs-tooltip-auto[x-placement^=bottom] .arrow, .bs-tooltip-bottom .arrow { - top: 0 -} - -.bs-tooltip-auto[x-placement^=bottom] .arrow:before, .bs-tooltip-bottom .arrow:before { - bottom: 0; - border-width: 0 .4rem .4rem; - border-bottom-color: rgba(97, 97, 97, .9) -} - -.bs-tooltip-auto[x-placement^=left], .bs-tooltip-left { - padding: 0 .4rem -} - -.bs-tooltip-auto[x-placement^=left] .arrow, .bs-tooltip-left .arrow { - right: 0; - width: .4rem; - height: .8rem -} - -.bs-tooltip-auto[x-placement^=left] .arrow:before, .bs-tooltip-left .arrow:before { - left: 0; - border-width: .4rem 0 .4rem .4rem; - border-left-color: rgba(97, 97, 97, .9) -} - -.tooltip-inner { - max-width: 200px; - padding: .25rem .5rem; - color: #fff; - text-align: center; - background-color: rgba(97, 97, 97, .9); - border-radius: .125rem -} - -.popover { - top: 0; - left: 0; - z-index: 1060; - max-width: 276px; - font-family: Roboto, Helvetica, Arial, sans-serif; - font-style: normal; - font-weight: 400; - line-height: 1.5; - text-align: left; - text-align: start; - text-decoration: none; - text-shadow: none; - text-transform: none; - letter-spacing: normal; - word-break: normal; - word-spacing: normal; - white-space: normal; - line-break: auto; - font-size: .875rem; - word-wrap: break-word; - background-color: #fff; - background-clip: padding-box; - border: 1px solid rgba(0, 0, 0, .2); - border-radius: .3rem; - box-shadow: 0 .25rem .5rem rgba(0, 0, 0, .2) -} - -.popover, .popover .arrow { - position: absolute; - display: block -} - -.popover .arrow { - width: 1rem; - height: .5rem; - margin: 0 .3rem -} - -.popover .arrow:after, .popover .arrow:before { - position: absolute; - display: block; - content: ""; - border-color: transparent; - border-style: solid -} - -.bs-popover-auto[x-placement^=top], .bs-popover-top { - margin-bottom: .5rem -} - -.bs-popover-auto[x-placement^=top] .arrow, .bs-popover-top .arrow { - bottom: calc((.5rem + 1px) * -1) -} - -.bs-popover-auto[x-placement^=top] .arrow:after, .bs-popover-auto[x-placement^=top] .arrow:before, .bs-popover-top .arrow:after, .bs-popover-top .arrow:before { - border-width: .5rem .5rem 0 -} - -.bs-popover-auto[x-placement^=top] .arrow:before, .bs-popover-top .arrow:before { - bottom: 0; - border-top-color: rgba(0, 0, 0, .25) -} - -.bs-popover-auto[x-placement^=top] .arrow:after, .bs-popover-top .arrow:after { - bottom: 1px; - border-top-color: #fff -} - -.bs-popover-auto[x-placement^=right], .bs-popover-right { - margin-left: .5rem -} - -.bs-popover-auto[x-placement^=right] .arrow, .bs-popover-right .arrow { - left: calc((.5rem + 1px) * -1); - width: .5rem; - height: 1rem; - margin: .3rem 0 -} - -.bs-popover-auto[x-placement^=right] .arrow:after, .bs-popover-auto[x-placement^=right] .arrow:before, .bs-popover-right .arrow:after, .bs-popover-right .arrow:before { - border-width: .5rem .5rem .5rem 0 -} - -.bs-popover-auto[x-placement^=right] .arrow:before, .bs-popover-right .arrow:before { - left: 0; - border-right-color: rgba(0, 0, 0, .25) -} - -.bs-popover-auto[x-placement^=right] .arrow:after, .bs-popover-right .arrow:after { - left: 1px; - border-right-color: #fff -} - -.bs-popover-auto[x-placement^=bottom], .bs-popover-bottom { - margin-top: .5rem -} - -.bs-popover-auto[x-placement^=bottom] .arrow, .bs-popover-bottom .arrow { - top: calc((.5rem + 1px) * -1) -} - -.bs-popover-auto[x-placement^=bottom] .arrow:after, .bs-popover-auto[x-placement^=bottom] .arrow:before, .bs-popover-bottom .arrow:after, .bs-popover-bottom .arrow:before { - border-width: 0 .5rem .5rem -} - -.bs-popover-auto[x-placement^=bottom] .arrow:before, .bs-popover-bottom .arrow:before { - top: 0; - border-bottom-color: rgba(0, 0, 0, .25) -} - -.bs-popover-auto[x-placement^=bottom] .arrow:after, .bs-popover-bottom .arrow:after { - top: 1px; - border-bottom-color: #fff -} - -.bs-popover-auto[x-placement^=bottom] .popover-header:before, .bs-popover-bottom .popover-header:before { - position: absolute; - top: 0; - left: 50%; - display: block; - width: 1rem; - margin-left: -.5rem; - content: ""; - border-bottom: 1px solid #f7f7f7 -} - -.bs-popover-auto[x-placement^=left], .bs-popover-left { - margin-right: .5rem -} - -.bs-popover-auto[x-placement^=left] .arrow, .bs-popover-left .arrow { - right: calc((.5rem + 1px) * -1); - width: .5rem; - height: 1rem; - margin: .3rem 0 -} - -.bs-popover-auto[x-placement^=left] .arrow:after, .bs-popover-auto[x-placement^=left] .arrow:before, .bs-popover-left .arrow:after, .bs-popover-left .arrow:before { - border-width: .5rem 0 .5rem .5rem -} - -.bs-popover-auto[x-placement^=left] .arrow:before, .bs-popover-left .arrow:before { - right: 0; - border-left-color: rgba(0, 0, 0, .25) -} - -.bs-popover-auto[x-placement^=left] .arrow:after, .bs-popover-left .arrow:after { - right: 1px; - border-left-color: #fff -} - -.popover-header { - padding: .5rem .75rem; - margin-bottom: 0; - font-size: 1rem; - color: inherit; - background-color: #f7f7f7; - border-bottom: 1px solid #ebebeb; - border-top-left-radius: calc(.3rem - 1px); - border-top-right-radius: calc(.3rem - 1px) -} - -.popover-header:empty { - display: none -} - -.popover-body { - padding: .5rem .75rem; - color: #212529 -} - -.carousel { - position: relative -} - -.carousel-inner { - position: relative; - width: 100%; - overflow: hidden -} - -.carousel-item { - position: relative; - display: none; - align-items: center; - width: 100%; - transition: transform .6s ease; - backface-visibility: hidden; - perspective: 1000px -} - -.carousel-item-next, .carousel-item-prev, .carousel-item.active { - display: block -} - -.carousel-item-next, .carousel-item-prev { - position: absolute; - top: 0 -} - -.carousel-item-next.carousel-item-left, .carousel-item-prev.carousel-item-right { - transform: translateX(0) -} - -@supports (transform-style:preserve-3d) { - .carousel-item-next.carousel-item-left, .carousel-item-prev.carousel-item-right { - transform: translateZ(0) - } -} - -.active.carousel-item-right, .carousel-item-next { - transform: translateX(100%) -} - -@supports (transform-style:preserve-3d) { - .active.carousel-item-right, .carousel-item-next { - transform: translate3d(100%, 0, 0) - } -} - -.active.carousel-item-left, .carousel-item-prev { - transform: translateX(-100%) -} - -@supports (transform-style:preserve-3d) { - .active.carousel-item-left, .carousel-item-prev { - transform: translate3d(-100%, 0, 0) - } -} - -.carousel-control-next, .carousel-control-prev { - position: absolute; - top: 0; - bottom: 0; - display: flex; - align-items: center; - justify-content: center; - width: 15%; - color: #fff; - text-align: center; - opacity: .5 -} - -.carousel-control-next:focus, .carousel-control-next:hover, .carousel-control-prev:focus, .carousel-control-prev:hover { - color: #fff; - text-decoration: none; - outline: 0; - opacity: .9 -} - -.carousel-control-prev { - left: 0 -} - -.carousel-control-next { - right: 0 -} - -.carousel-control-next-icon, .carousel-control-prev-icon { - display: inline-block; - width: 20px; - height: 20px; - background: transparent no-repeat 50%; - background-size: 100% 100% -} - -.carousel-control-prev-icon { - background-image: url("data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' fill='%23fff' viewBox='0 0 8 8'%3E%3Cpath d='M5.25 0l-4 4 4 4 1.5-1.5-2.5-2.5 2.5-2.5-1.5-1.5z'/%3E%3C/svg%3E") -} - -.carousel-control-next-icon { - background-image: url("data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' fill='%23fff' viewBox='0 0 8 8'%3E%3Cpath d='M2.75 0l-1.5 1.5 2.5 2.5-2.5 2.5 1.5 1.5 4-4-4-4z'/%3E%3C/svg%3E") -} - -.carousel-indicators { - position: absolute; - right: 0; - bottom: 10px; - left: 0; - z-index: 15; - display: flex; - justify-content: center; - padding-left: 0; - margin-right: 15%; - margin-left: 15%; - list-style: none -} - -.carousel-indicators li { - position: relative; - flex: 0 1 auto; - width: 30px; - height: 3px; - margin-right: 3px; - margin-left: 3px; - text-indent: -999px; - background-color: hsla(0, 0%, 100%, .5) -} - -.carousel-indicators li:before { - top: -10px -} - -.carousel-indicators li:after, .carousel-indicators li:before { - position: absolute; - left: 0; - display: inline-block; - width: 100%; - height: 10px; - content: "" -} - -.carousel-indicators li:after { - bottom: -10px -} - -.carousel-indicators .active { - background-color: #fff -} - -.carousel-caption { - position: absolute; - right: 15%; - bottom: 20px; - left: 15%; - z-index: 10; - padding-top: 20px; - padding-bottom: 20px; - color: #fff; - text-align: center -} - -.align-baseline { - vertical-align: baseline !important -} - -.align-top { - vertical-align: top !important -} - -.align-middle { - vertical-align: middle !important -} - -.align-bottom { - vertical-align: bottom !important -} - -.align-text-bottom { - vertical-align: text-bottom !important -} - -.align-text-top { - vertical-align: text-top !important -} - -.bg-primary { - background-color: #009688 !important -} - -a.bg-primary:focus, a.bg-primary:hover, button.bg-primary:focus, button.bg-primary:hover { - background-color: #00635a !important -} - -.bg-secondary { - background-color: #6c757d !important -} - -a.bg-secondary:focus, a.bg-secondary:hover, button.bg-secondary:focus, button.bg-secondary:hover { - background-color: #545b62 !important -} - -.bg-success { - background-color: #4caf50 !important -} - -a.bg-success:focus, a.bg-success:hover, button.bg-success:focus, button.bg-success:hover { - background-color: #3d8b40 !important -} - -.bg-info { - background-color: #03a9f4 !important -} - -a.bg-info:focus, a.bg-info:hover, button.bg-info:focus, button.bg-info:hover { - background-color: #0286c2 !important -} - -.bg-warning { - background-color: #ff5722 !important -} - -a.bg-warning:focus, a.bg-warning:hover, button.bg-warning:focus, button.bg-warning:hover { - background-color: #ee3900 !important -} - -.bg-danger { - background-color: #f44336 !important -} - -a.bg-danger:focus, a.bg-danger:hover, button.bg-danger:focus, button.bg-danger:hover { - background-color: #ea1c0d !important -} - -.bg-light { - background-color: #f5f5f5 !important -} - -a.bg-light:focus, a.bg-light:hover, button.bg-light:focus, button.bg-light:hover { - background-color: #dcdcdc !important -} - -.bg-dark { - background-color: #424242 !important -} - -a.bg-dark:focus, a.bg-dark:hover, button.bg-dark:focus, button.bg-dark:hover { - background-color: #292929 !important -} - -.bg-white { - background-color: #fff !important -} - -.bg-transparent { - background-color: transparent !important -} - -.border { - border: 1px solid #dee2e6 !important -} - -.border-top { - border-top: 1px solid #dee2e6 !important -} - -.border-right { - border-right: 1px solid #dee2e6 !important -} - -.border-bottom { - border-bottom: 1px solid #dee2e6 !important -} - -.border-left { - border-left: 1px solid #dee2e6 !important -} - -.border-0 { - border: 0 !important -} - -.border-top-0 { - border-top: 0 !important -} - -.border-right-0 { - border-right: 0 !important -} - -.border-bottom-0 { - border-bottom: 0 !important -} - -.border-left-0 { - border-left: 0 !important -} - -.border-primary { - border-color: #009688 !important -} - -.border-secondary { - border-color: #6c757d !important -} - -.border-success { - border-color: #4caf50 !important -} - -.border-info { - border-color: #03a9f4 !important -} - -.border-warning { - border-color: #ff5722 !important -} - -.border-danger { - border-color: #f44336 !important -} - -.border-light { - border-color: #f5f5f5 !important -} - -.border-dark { - border-color: #424242 !important -} - -.border-white { - border-color: #fff !important -} - -.rounded { - border-radius: .125rem !important -} - -.rounded-top { - border-top-left-radius: .125rem !important -} - -.rounded-right, .rounded-top { - border-top-right-radius: .125rem !important -} - -.rounded-bottom, .rounded-right { - border-bottom-right-radius: .125rem !important -} - -.rounded-bottom, .rounded-left { - border-bottom-left-radius: .125rem !important -} - -.rounded-left { - border-top-left-radius: .125rem !important -} - -.rounded-circle { - border-radius: 50% !important -} - -.rounded-0 { - border-radius: 0 !important -} - -.clearfix:after { - display: block; - clear: both; - content: "" -} - -.d-none { - display: none !important -} - -.d-inline { - display: inline !important -} - -.d-inline-block { - display: inline-block !important -} - -.d-block { - display: block !important -} - -.d-table { - display: table !important -} - -.d-table-row { - display: table-row !important -} - -.d-table-cell { - display: table-cell !important -} - -.d-flex { - display: flex !important -} - -.d-inline-flex { - display: inline-flex !important -} - -@media (min-width: 576px) { - .d-sm-none { - display: none !important - } - - .d-sm-inline { - display: inline !important - } - - .d-sm-inline-block { - display: inline-block !important - } - - .d-sm-block { - display: block !important - } - - .d-sm-table { - display: table !important - } - - .d-sm-table-row { - display: table-row !important - } - - .d-sm-table-cell { - display: table-cell !important - } - - .d-sm-flex { - display: flex !important - } - - .d-sm-inline-flex { - display: inline-flex !important - } -} - -@media (min-width: 768px) { - .d-md-none { - display: none !important - } - - .d-md-inline { - display: inline !important - } - - .d-md-inline-block { - display: inline-block !important - } - - .d-md-block { - display: block !important - } - - .d-md-table { - display: table !important - } - - .d-md-table-row { - display: table-row !important - } - - .d-md-table-cell { - display: table-cell !important - } - - .d-md-flex { - display: flex !important - } - - .d-md-inline-flex { - display: inline-flex !important - } -} - -@media (min-width: 992px) { - .d-lg-none { - display: none !important - } - - .d-lg-inline { - display: inline !important - } - - .d-lg-inline-block { - display: inline-block !important - } - - .d-lg-block { - display: block !important - } - - .d-lg-table { - display: table !important - } - - .d-lg-table-row { - display: table-row !important - } - - .d-lg-table-cell { - display: table-cell !important - } - - .d-lg-flex { - display: flex !important - } - - .d-lg-inline-flex { - display: inline-flex !important - } -} - -@media (min-width: 1200px) { - .d-xl-none { - display: none !important - } - - .d-xl-inline { - display: inline !important - } - - .d-xl-inline-block { - display: inline-block !important - } - - .d-xl-block { - display: block !important - } - - .d-xl-table { - display: table !important - } - - .d-xl-table-row { - display: table-row !important - } - - .d-xl-table-cell { - display: table-cell !important - } - - .d-xl-flex { - display: flex !important - } - - .d-xl-inline-flex { - display: inline-flex !important - } -} - -@media print { - .d-print-none { - display: none !important - } - - .d-print-inline { - display: inline !important - } - - .d-print-inline-block { - display: inline-block !important - } - - .d-print-block { - display: block !important - } - - .d-print-table { - display: table !important - } - - .d-print-table-row { - display: table-row !important - } - - .d-print-table-cell { - display: table-cell !important - } - - .d-print-flex { - display: flex !important - } - - .d-print-inline-flex { - display: inline-flex !important - } -} - -.embed-responsive { - position: relative; - display: block; - width: 100%; - padding: 0; - overflow: hidden -} - -.embed-responsive:before { - display: block; - content: "" -} - -.embed-responsive .embed-responsive-item, .embed-responsive embed, .embed-responsive iframe, .embed-responsive object, .embed-responsive video { - position: absolute; - top: 0; - bottom: 0; - left: 0; - width: 100%; - height: 100%; - border: 0 -} - -.embed-responsive-21by9:before { - padding-top: 42.85714% -} - -.embed-responsive-16by9:before { - padding-top: 56.25% -} - -.embed-responsive-4by3:before { - padding-top: 75% -} - -.embed-responsive-1by1:before { - padding-top: 100% -} - -.flex-row { - flex-direction: row !important -} - -.flex-column { - flex-direction: column !important -} - -.flex-row-reverse { - flex-direction: row-reverse !important -} - -.flex-column-reverse { - flex-direction: column-reverse !important -} - -.flex-wrap { - flex-wrap: wrap !important -} - -.flex-nowrap { - flex-wrap: nowrap !important -} - -.flex-wrap-reverse { - flex-wrap: wrap-reverse !important -} - -.justify-content-start { - justify-content: flex-start !important -} - -.justify-content-end { - justify-content: flex-end !important -} - -.justify-content-center { - justify-content: center !important -} - -.justify-content-between { - justify-content: space-between !important -} - -.justify-content-around { - justify-content: space-around !important -} - -.align-items-start { - align-items: flex-start !important -} - -.align-items-end { - align-items: flex-end !important -} - -.align-items-center { - align-items: center !important -} - -.align-items-baseline { - align-items: baseline !important -} - -.align-items-stretch { - align-items: stretch !important -} - -.align-content-start { - align-content: flex-start !important -} - -.align-content-end { - align-content: flex-end !important -} - -.align-content-center { - align-content: center !important -} - -.align-content-between { - align-content: space-between !important -} - -.align-content-around { - align-content: space-around !important -} - -.align-content-stretch { - align-content: stretch !important -} - -.align-self-auto { - align-self: auto !important -} - -.align-self-start { - align-self: flex-start !important -} - -.align-self-end { - align-self: flex-end !important -} - -.align-self-center { - align-self: center !important -} - -.align-self-baseline { - align-self: baseline !important -} - -.align-self-stretch { - align-self: stretch !important -} - -@media (min-width: 576px) { - .flex-sm-row { - flex-direction: row !important - } - - .flex-sm-column { - flex-direction: column !important - } - - .flex-sm-row-reverse { - flex-direction: row-reverse !important - } - - .flex-sm-column-reverse { - flex-direction: column-reverse !important - } - - .flex-sm-wrap { - flex-wrap: wrap !important - } - - .flex-sm-nowrap { - flex-wrap: nowrap !important - } - - .flex-sm-wrap-reverse { - flex-wrap: wrap-reverse !important - } - - .justify-content-sm-start { - justify-content: flex-start !important - } - - .justify-content-sm-end { - justify-content: flex-end !important - } - - .justify-content-sm-center { - justify-content: center !important - } - - .justify-content-sm-between { - justify-content: space-between !important - } - - .justify-content-sm-around { - justify-content: space-around !important - } - - .align-items-sm-start { - align-items: flex-start !important - } - - .align-items-sm-end { - align-items: flex-end !important - } - - .align-items-sm-center { - align-items: center !important - } - - .align-items-sm-baseline { - align-items: baseline !important - } - - .align-items-sm-stretch { - align-items: stretch !important - } - - .align-content-sm-start { - align-content: flex-start !important - } - - .align-content-sm-end { - align-content: flex-end !important - } - - .align-content-sm-center { - align-content: center !important - } - - .align-content-sm-between { - align-content: space-between !important - } - - .align-content-sm-around { - align-content: space-around !important - } - - .align-content-sm-stretch { - align-content: stretch !important - } - - .align-self-sm-auto { - align-self: auto !important - } - - .align-self-sm-start { - align-self: flex-start !important - } - - .align-self-sm-end { - align-self: flex-end !important - } - - .align-self-sm-center { - align-self: center !important - } - - .align-self-sm-baseline { - align-self: baseline !important - } - - .align-self-sm-stretch { - align-self: stretch !important - } -} - -@media (min-width: 768px) { - .flex-md-row { - flex-direction: row !important - } - - .flex-md-column { - flex-direction: column !important - } - - .flex-md-row-reverse { - flex-direction: row-reverse !important - } - - .flex-md-column-reverse { - flex-direction: column-reverse !important - } - - .flex-md-wrap { - flex-wrap: wrap !important - } - - .flex-md-nowrap { - flex-wrap: nowrap !important - } - - .flex-md-wrap-reverse { - flex-wrap: wrap-reverse !important - } - - .justify-content-md-start { - justify-content: flex-start !important - } - - .justify-content-md-end { - justify-content: flex-end !important - } - - .justify-content-md-center { - justify-content: center !important - } - - .justify-content-md-between { - justify-content: space-between !important - } - - .justify-content-md-around { - justify-content: space-around !important - } - - .align-items-md-start { - align-items: flex-start !important - } - - .align-items-md-end { - align-items: flex-end !important - } - - .align-items-md-center { - align-items: center !important - } - - .align-items-md-baseline { - align-items: baseline !important - } - - .align-items-md-stretch { - align-items: stretch !important - } - - .align-content-md-start { - align-content: flex-start !important - } - - .align-content-md-end { - align-content: flex-end !important - } - - .align-content-md-center { - align-content: center !important - } - - .align-content-md-between { - align-content: space-between !important - } - - .align-content-md-around { - align-content: space-around !important - } - - .align-content-md-stretch { - align-content: stretch !important - } - - .align-self-md-auto { - align-self: auto !important - } - - .align-self-md-start { - align-self: flex-start !important - } - - .align-self-md-end { - align-self: flex-end !important - } - - .align-self-md-center { - align-self: center !important - } - - .align-self-md-baseline { - align-self: baseline !important - } - - .align-self-md-stretch { - align-self: stretch !important - } -} - -@media (min-width: 992px) { - .flex-lg-row { - flex-direction: row !important - } - - .flex-lg-column { - flex-direction: column !important - } - - .flex-lg-row-reverse { - flex-direction: row-reverse !important - } - - .flex-lg-column-reverse { - flex-direction: column-reverse !important - } - - .flex-lg-wrap { - flex-wrap: wrap !important - } - - .flex-lg-nowrap { - flex-wrap: nowrap !important - } - - .flex-lg-wrap-reverse { - flex-wrap: wrap-reverse !important - } - - .justify-content-lg-start { - justify-content: flex-start !important - } - - .justify-content-lg-end { - justify-content: flex-end !important - } - - .justify-content-lg-center { - justify-content: center !important - } - - .justify-content-lg-between { - justify-content: space-between !important - } - - .justify-content-lg-around { - justify-content: space-around !important - } - - .align-items-lg-start { - align-items: flex-start !important - } - - .align-items-lg-end { - align-items: flex-end !important - } - - .align-items-lg-center { - align-items: center !important - } - - .align-items-lg-baseline { - align-items: baseline !important - } - - .align-items-lg-stretch { - align-items: stretch !important - } - - .align-content-lg-start { - align-content: flex-start !important - } - - .align-content-lg-end { - align-content: flex-end !important - } - - .align-content-lg-center { - align-content: center !important - } - - .align-content-lg-between { - align-content: space-between !important - } - - .align-content-lg-around { - align-content: space-around !important - } - - .align-content-lg-stretch { - align-content: stretch !important - } - - .align-self-lg-auto { - align-self: auto !important - } - - .align-self-lg-start { - align-self: flex-start !important - } - - .align-self-lg-end { - align-self: flex-end !important - } - - .align-self-lg-center { - align-self: center !important - } - - .align-self-lg-baseline { - align-self: baseline !important - } - - .align-self-lg-stretch { - align-self: stretch !important - } -} - -@media (min-width: 1200px) { - .flex-xl-row { - flex-direction: row !important - } - - .flex-xl-column { - flex-direction: column !important - } - - .flex-xl-row-reverse { - flex-direction: row-reverse !important - } - - .flex-xl-column-reverse { - flex-direction: column-reverse !important - } - - .flex-xl-wrap { - flex-wrap: wrap !important - } - - .flex-xl-nowrap { - flex-wrap: nowrap !important - } - - .flex-xl-wrap-reverse { - flex-wrap: wrap-reverse !important - } - - .justify-content-xl-start { - justify-content: flex-start !important - } - - .justify-content-xl-end { - justify-content: flex-end !important - } - - .justify-content-xl-center { - justify-content: center !important - } - - .justify-content-xl-between { - justify-content: space-between !important - } - - .justify-content-xl-around { - justify-content: space-around !important - } - - .align-items-xl-start { - align-items: flex-start !important - } - - .align-items-xl-end { - align-items: flex-end !important - } - - .align-items-xl-center { - align-items: center !important - } - - .align-items-xl-baseline { - align-items: baseline !important - } - - .align-items-xl-stretch { - align-items: stretch !important - } - - .align-content-xl-start { - align-content: flex-start !important - } - - .align-content-xl-end { - align-content: flex-end !important - } - - .align-content-xl-center { - align-content: center !important - } - - .align-content-xl-between { - align-content: space-between !important - } - - .align-content-xl-around { - align-content: space-around !important - } - - .align-content-xl-stretch { - align-content: stretch !important - } - - .align-self-xl-auto { - align-self: auto !important - } - - .align-self-xl-start { - align-self: flex-start !important - } - - .align-self-xl-end { - align-self: flex-end !important - } - - .align-self-xl-center { - align-self: center !important - } - - .align-self-xl-baseline { - align-self: baseline !important - } - - .align-self-xl-stretch { - align-self: stretch !important - } -} - -.float-left { - float: left !important -} - -.float-right { - float: right !important -} - -.float-none { - float: none !important -} - -@media (min-width: 576px) { - .float-sm-left { - float: left !important - } - - .float-sm-right { - float: right !important - } - - .float-sm-none { - float: none !important - } -} - -@media (min-width: 768px) { - .float-md-left { - float: left !important - } - - .float-md-right { - float: right !important - } - - .float-md-none { - float: none !important - } -} - -@media (min-width: 992px) { - .float-lg-left { - float: left !important - } - - .float-lg-right { - float: right !important - } - - .float-lg-none { - float: none !important - } -} - -@media (min-width: 1200px) { - .float-xl-left { - float: left !important - } - - .float-xl-right { - float: right !important - } - - .float-xl-none { - float: none !important - } -} - -.position-static { - position: static !important -} - -.position-relative { - position: relative !important -} - -.position-absolute { - position: absolute !important -} - -.position-fixed { - position: fixed !important -} - -.position-sticky { - position: sticky !important -} - -.fixed-top { - top: 0 -} - -.fixed-bottom, .fixed-top { - position: fixed; - right: 0; - left: 0; - z-index: 1030 -} - -.fixed-bottom { - bottom: 0 -} - -@supports (position:sticky) { - .sticky-top { - position: sticky; - top: 0; - z-index: 1020 - } -} - -.sr-only { - position: absolute; - width: 1px; - height: 1px; - padding: 0; - overflow: hidden; - clip: rect(0, 0, 0, 0); - white-space: nowrap; - clip-path: inset(50%); - border: 0 -} - -.sr-only-focusable:active, .sr-only-focusable:focus { - position: static; - width: auto; - height: auto; - overflow: visible; - clip: auto; - white-space: normal; - clip-path: none -} - -.w-25 { - width: 25% !important -} - -.w-50 { - width: 50% !important -} - -.w-75 { - width: 75% !important -} - -.w-100 { - width: 100% !important -} - -.h-25 { - height: 25% !important -} - -.h-50 { - height: 50% !important -} - -.h-75 { - height: 75% !important -} - -.h-100 { - height: 100% !important -} - -.mw-100 { - max-width: 100% !important -} - -.mh-100 { - max-height: 100% !important -} - -.m-0 { - margin: 0 !important -} - -.mt-0, .my-0 { - margin-top: 0 !important -} - -.mr-0, .mx-0 { - margin-right: 0 !important -} - -.mb-0, .my-0 { - margin-bottom: 0 !important -} - -.ml-0, .mx-0 { - margin-left: 0 !important -} - -.m-1 { - margin: .25rem !important -} - -.mt-1, .my-1 { - margin-top: .25rem !important -} - -.mr-1, .mx-1 { - margin-right: .25rem !important -} - -.mb-1, .my-1 { - margin-bottom: .25rem !important -} - -.ml-1, .mx-1 { - margin-left: .25rem !important -} - -.m-2 { - margin: .5rem !important -} - -.mt-2, .my-2 { - margin-top: .5rem !important -} - -.mr-2, .mx-2 { - margin-right: .5rem !important -} - -.mb-2, .my-2 { - margin-bottom: .5rem !important -} - -.ml-2, .mx-2 { - margin-left: .5rem !important -} - -.m-3 { - margin: 1rem !important -} - -.mt-3, .my-3 { - margin-top: 1rem !important -} - -.mr-3, .mx-3 { - margin-right: 1rem !important -} - -.mb-3, .my-3 { - margin-bottom: 1rem !important -} - -.ml-3, .mx-3 { - margin-left: 1rem !important -} - -.m-4 { - margin: 1.5rem !important -} - -.mt-4, .my-4 { - margin-top: 1.5rem !important -} - -.mr-4, .mx-4 { - margin-right: 1.5rem !important -} - -.mb-4, .my-4 { - margin-bottom: 1.5rem !important -} - -.ml-4, .mx-4 { - margin-left: 1.5rem !important -} - -.m-5 { - margin: 3rem !important -} - -.mt-5, .my-5 { - margin-top: 3rem !important -} - -.mr-5, .mx-5 { - margin-right: 3rem !important -} - -.mb-5, .my-5 { - margin-bottom: 3rem !important -} - -.ml-5, .mx-5 { - margin-left: 3rem !important -} - -.p-0 { - padding: 0 !important -} - -.pt-0, .py-0 { - padding-top: 0 !important -} - -.pr-0, .px-0 { - padding-right: 0 !important -} - -.pb-0, .py-0 { - padding-bottom: 0 !important -} - -.pl-0, .px-0 { - padding-left: 0 !important -} - -.p-1 { - padding: .25rem !important -} - -.pt-1, .py-1 { - padding-top: .25rem !important -} - -.pr-1, .px-1 { - padding-right: .25rem !important -} - -.pb-1, .py-1 { - padding-bottom: .25rem !important -} - -.pl-1, .px-1 { - padding-left: .25rem !important -} - -.p-2 { - padding: .5rem !important -} - -.pt-2, .py-2 { - padding-top: .5rem !important -} - -.pr-2, .px-2 { - padding-right: .5rem !important -} - -.pb-2, .py-2 { - padding-bottom: .5rem !important -} - -.pl-2, .px-2 { - padding-left: .5rem !important -} - -.p-3 { - padding: 1rem !important -} - -.pt-3, .py-3 { - padding-top: 1rem !important -} - -.pr-3, .px-3 { - padding-right: 1rem !important -} - -.pb-3, .py-3 { - padding-bottom: 1rem !important -} - -.pl-3, .px-3 { - padding-left: 1rem !important -} - -.p-4 { - padding: 1.5rem !important -} - -.pt-4, .py-4 { - padding-top: 1.5rem !important -} - -.pr-4, .px-4 { - padding-right: 1.5rem !important -} - -.pb-4, .py-4 { - padding-bottom: 1.5rem !important -} - -.pl-4, .px-4 { - padding-left: 1.5rem !important -} - -.p-5 { - padding: 3rem !important -} - -.pt-5, .py-5 { - padding-top: 3rem !important -} - -.pr-5, .px-5 { - padding-right: 3rem !important -} - -.pb-5, .py-5 { - padding-bottom: 3rem !important -} - -.pl-5, .px-5 { - padding-left: 3rem !important -} - -.m-auto { - margin: auto !important -} - -.mt-auto, .my-auto { - margin-top: auto !important -} - -.mr-auto, .mx-auto { - margin-right: auto !important -} - -.mb-auto, .my-auto { - margin-bottom: auto !important -} - -.ml-auto, .mx-auto { - margin-left: auto !important -} - -@media (min-width: 576px) { - .m-sm-0 { - margin: 0 !important - } - - .mt-sm-0, .my-sm-0 { - margin-top: 0 !important - } - - .mr-sm-0, .mx-sm-0 { - margin-right: 0 !important - } - - .mb-sm-0, .my-sm-0 { - margin-bottom: 0 !important - } - - .ml-sm-0, .mx-sm-0 { - margin-left: 0 !important - } - - .m-sm-1 { - margin: .25rem !important - } - - .mt-sm-1, .my-sm-1 { - margin-top: .25rem !important - } - - .mr-sm-1, .mx-sm-1 { - margin-right: .25rem !important - } - - .mb-sm-1, .my-sm-1 { - margin-bottom: .25rem !important - } - - .ml-sm-1, .mx-sm-1 { - margin-left: .25rem !important - } - - .m-sm-2 { - margin: .5rem !important - } - - .mt-sm-2, .my-sm-2 { - margin-top: .5rem !important - } - - .mr-sm-2, .mx-sm-2 { - margin-right: .5rem !important - } - - .mb-sm-2, .my-sm-2 { - margin-bottom: .5rem !important - } - - .ml-sm-2, .mx-sm-2 { - margin-left: .5rem !important - } - - .m-sm-3 { - margin: 1rem !important - } - - .mt-sm-3, .my-sm-3 { - margin-top: 1rem !important - } - - .mr-sm-3, .mx-sm-3 { - margin-right: 1rem !important - } - - .mb-sm-3, .my-sm-3 { - margin-bottom: 1rem !important - } - - .ml-sm-3, .mx-sm-3 { - margin-left: 1rem !important - } - - .m-sm-4 { - margin: 1.5rem !important - } - - .mt-sm-4, .my-sm-4 { - margin-top: 1.5rem !important - } - - .mr-sm-4, .mx-sm-4 { - margin-right: 1.5rem !important - } - - .mb-sm-4, .my-sm-4 { - margin-bottom: 1.5rem !important - } - - .ml-sm-4, .mx-sm-4 { - margin-left: 1.5rem !important - } - - .m-sm-5 { - margin: 3rem !important - } - - .mt-sm-5, .my-sm-5 { - margin-top: 3rem !important - } - - .mr-sm-5, .mx-sm-5 { - margin-right: 3rem !important - } - - .mb-sm-5, .my-sm-5 { - margin-bottom: 3rem !important - } - - .ml-sm-5, .mx-sm-5 { - margin-left: 3rem !important - } - - .p-sm-0 { - padding: 0 !important - } - - .pt-sm-0, .py-sm-0 { - padding-top: 0 !important - } - - .pr-sm-0, .px-sm-0 { - padding-right: 0 !important - } - - .pb-sm-0, .py-sm-0 { - padding-bottom: 0 !important - } - - .pl-sm-0, .px-sm-0 { - padding-left: 0 !important - } - - .p-sm-1 { - padding: .25rem !important - } - - .pt-sm-1, .py-sm-1 { - padding-top: .25rem !important - } - - .pr-sm-1, .px-sm-1 { - padding-right: .25rem !important - } - - .pb-sm-1, .py-sm-1 { - padding-bottom: .25rem !important - } - - .pl-sm-1, .px-sm-1 { - padding-left: .25rem !important - } - - .p-sm-2 { - padding: .5rem !important - } - - .pt-sm-2, .py-sm-2 { - padding-top: .5rem !important - } - - .pr-sm-2, .px-sm-2 { - padding-right: .5rem !important - } - - .pb-sm-2, .py-sm-2 { - padding-bottom: .5rem !important - } - - .pl-sm-2, .px-sm-2 { - padding-left: .5rem !important - } - - .p-sm-3 { - padding: 1rem !important - } - - .pt-sm-3, .py-sm-3 { - padding-top: 1rem !important - } - - .pr-sm-3, .px-sm-3 { - padding-right: 1rem !important - } - - .pb-sm-3, .py-sm-3 { - padding-bottom: 1rem !important - } - - .pl-sm-3, .px-sm-3 { - padding-left: 1rem !important - } - - .p-sm-4 { - padding: 1.5rem !important - } - - .pt-sm-4, .py-sm-4 { - padding-top: 1.5rem !important - } - - .pr-sm-4, .px-sm-4 { - padding-right: 1.5rem !important - } - - .pb-sm-4, .py-sm-4 { - padding-bottom: 1.5rem !important - } - - .pl-sm-4, .px-sm-4 { - padding-left: 1.5rem !important - } - - .p-sm-5 { - padding: 3rem !important - } - - .pt-sm-5, .py-sm-5 { - padding-top: 3rem !important - } - - .pr-sm-5, .px-sm-5 { - padding-right: 3rem !important - } - - .pb-sm-5, .py-sm-5 { - padding-bottom: 3rem !important - } - - .pl-sm-5, .px-sm-5 { - padding-left: 3rem !important - } - - .m-sm-auto { - margin: auto !important - } - - .mt-sm-auto, .my-sm-auto { - margin-top: auto !important - } - - .mr-sm-auto, .mx-sm-auto { - margin-right: auto !important - } - - .mb-sm-auto, .my-sm-auto { - margin-bottom: auto !important - } - - .ml-sm-auto, .mx-sm-auto { - margin-left: auto !important - } -} - -@media (min-width: 768px) { - .m-md-0 { - margin: 0 !important - } - - .mt-md-0, .my-md-0 { - margin-top: 0 !important - } - - .mr-md-0, .mx-md-0 { - margin-right: 0 !important - } - - .mb-md-0, .my-md-0 { - margin-bottom: 0 !important - } - - .ml-md-0, .mx-md-0 { - margin-left: 0 !important - } - - .m-md-1 { - margin: .25rem !important - } - - .mt-md-1, .my-md-1 { - margin-top: .25rem !important - } - - .mr-md-1, .mx-md-1 { - margin-right: .25rem !important - } - - .mb-md-1, .my-md-1 { - margin-bottom: .25rem !important - } - - .ml-md-1, .mx-md-1 { - margin-left: .25rem !important - } - - .m-md-2 { - margin: .5rem !important - } - - .mt-md-2, .my-md-2 { - margin-top: .5rem !important - } - - .mr-md-2, .mx-md-2 { - margin-right: .5rem !important - } - - .mb-md-2, .my-md-2 { - margin-bottom: .5rem !important - } - - .ml-md-2, .mx-md-2 { - margin-left: .5rem !important - } - - .m-md-3 { - margin: 1rem !important - } - - .mt-md-3, .my-md-3 { - margin-top: 1rem !important - } - - .mr-md-3, .mx-md-3 { - margin-right: 1rem !important - } - - .mb-md-3, .my-md-3 { - margin-bottom: 1rem !important - } - - .ml-md-3, .mx-md-3 { - margin-left: 1rem !important - } - - .m-md-4 { - margin: 1.5rem !important - } - - .mt-md-4, .my-md-4 { - margin-top: 1.5rem !important - } - - .mr-md-4, .mx-md-4 { - margin-right: 1.5rem !important - } - - .mb-md-4, .my-md-4 { - margin-bottom: 1.5rem !important - } - - .ml-md-4, .mx-md-4 { - margin-left: 1.5rem !important - } - - .m-md-5 { - margin: 3rem !important - } - - .mt-md-5, .my-md-5 { - margin-top: 3rem !important - } - - .mr-md-5, .mx-md-5 { - margin-right: 3rem !important - } - - .mb-md-5, .my-md-5 { - margin-bottom: 3rem !important - } - - .ml-md-5, .mx-md-5 { - margin-left: 3rem !important - } - - .p-md-0 { - padding: 0 !important - } - - .pt-md-0, .py-md-0 { - padding-top: 0 !important - } - - .pr-md-0, .px-md-0 { - padding-right: 0 !important - } - - .pb-md-0, .py-md-0 { - padding-bottom: 0 !important - } - - .pl-md-0, .px-md-0 { - padding-left: 0 !important - } - - .p-md-1 { - padding: .25rem !important - } - - .pt-md-1, .py-md-1 { - padding-top: .25rem !important - } - - .pr-md-1, .px-md-1 { - padding-right: .25rem !important - } - - .pb-md-1, .py-md-1 { - padding-bottom: .25rem !important - } - - .pl-md-1, .px-md-1 { - padding-left: .25rem !important - } - - .p-md-2 { - padding: .5rem !important - } - - .pt-md-2, .py-md-2 { - padding-top: .5rem !important - } - - .pr-md-2, .px-md-2 { - padding-right: .5rem !important - } - - .pb-md-2, .py-md-2 { - padding-bottom: .5rem !important - } - - .pl-md-2, .px-md-2 { - padding-left: .5rem !important - } - - .p-md-3 { - padding: 1rem !important - } - - .pt-md-3, .py-md-3 { - padding-top: 1rem !important - } - - .pr-md-3, .px-md-3 { - padding-right: 1rem !important - } - - .pb-md-3, .py-md-3 { - padding-bottom: 1rem !important - } - - .pl-md-3, .px-md-3 { - padding-left: 1rem !important - } - - .p-md-4 { - padding: 1.5rem !important - } - - .pt-md-4, .py-md-4 { - padding-top: 1.5rem !important - } - - .pr-md-4, .px-md-4 { - padding-right: 1.5rem !important - } - - .pb-md-4, .py-md-4 { - padding-bottom: 1.5rem !important - } - - .pl-md-4, .px-md-4 { - padding-left: 1.5rem !important - } - - .p-md-5 { - padding: 3rem !important - } - - .pt-md-5, .py-md-5 { - padding-top: 3rem !important - } - - .pr-md-5, .px-md-5 { - padding-right: 3rem !important - } - - .pb-md-5, .py-md-5 { - padding-bottom: 3rem !important - } - - .pl-md-5, .px-md-5 { - padding-left: 3rem !important - } - - .m-md-auto { - margin: auto !important - } - - .mt-md-auto, .my-md-auto { - margin-top: auto !important - } - - .mr-md-auto, .mx-md-auto { - margin-right: auto !important - } - - .mb-md-auto, .my-md-auto { - margin-bottom: auto !important - } - - .ml-md-auto, .mx-md-auto { - margin-left: auto !important - } -} - -@media (min-width: 992px) { - .m-lg-0 { - margin: 0 !important - } - - .mt-lg-0, .my-lg-0 { - margin-top: 0 !important - } - - .mr-lg-0, .mx-lg-0 { - margin-right: 0 !important - } - - .mb-lg-0, .my-lg-0 { - margin-bottom: 0 !important - } - - .ml-lg-0, .mx-lg-0 { - margin-left: 0 !important - } - - .m-lg-1 { - margin: .25rem !important - } - - .mt-lg-1, .my-lg-1 { - margin-top: .25rem !important - } - - .mr-lg-1, .mx-lg-1 { - margin-right: .25rem !important - } - - .mb-lg-1, .my-lg-1 { - margin-bottom: .25rem !important - } - - .ml-lg-1, .mx-lg-1 { - margin-left: .25rem !important - } - - .m-lg-2 { - margin: .5rem !important - } - - .mt-lg-2, .my-lg-2 { - margin-top: .5rem !important - } - - .mr-lg-2, .mx-lg-2 { - margin-right: .5rem !important - } - - .mb-lg-2, .my-lg-2 { - margin-bottom: .5rem !important - } - - .ml-lg-2, .mx-lg-2 { - margin-left: .5rem !important - } - - .m-lg-3 { - margin: 1rem !important - } - - .mt-lg-3, .my-lg-3 { - margin-top: 1rem !important - } - - .mr-lg-3, .mx-lg-3 { - margin-right: 1rem !important - } - - .mb-lg-3, .my-lg-3 { - margin-bottom: 1rem !important - } - - .ml-lg-3, .mx-lg-3 { - margin-left: 1rem !important - } - - .m-lg-4 { - margin: 1.5rem !important - } - - .mt-lg-4, .my-lg-4 { - margin-top: 1.5rem !important - } - - .mr-lg-4, .mx-lg-4 { - margin-right: 1.5rem !important - } - - .mb-lg-4, .my-lg-4 { - margin-bottom: 1.5rem !important - } - - .ml-lg-4, .mx-lg-4 { - margin-left: 1.5rem !important - } - - .m-lg-5 { - margin: 3rem !important - } - - .mt-lg-5, .my-lg-5 { - margin-top: 3rem !important - } - - .mr-lg-5, .mx-lg-5 { - margin-right: 3rem !important - } - - .mb-lg-5, .my-lg-5 { - margin-bottom: 3rem !important - } - - .ml-lg-5, .mx-lg-5 { - margin-left: 3rem !important - } - - .p-lg-0 { - padding: 0 !important - } - - .pt-lg-0, .py-lg-0 { - padding-top: 0 !important - } - - .pr-lg-0, .px-lg-0 { - padding-right: 0 !important - } - - .pb-lg-0, .py-lg-0 { - padding-bottom: 0 !important - } - - .pl-lg-0, .px-lg-0 { - padding-left: 0 !important - } - - .p-lg-1 { - padding: .25rem !important - } - - .pt-lg-1, .py-lg-1 { - padding-top: .25rem !important - } - - .pr-lg-1, .px-lg-1 { - padding-right: .25rem !important - } - - .pb-lg-1, .py-lg-1 { - padding-bottom: .25rem !important - } - - .pl-lg-1, .px-lg-1 { - padding-left: .25rem !important - } - - .p-lg-2 { - padding: .5rem !important - } - - .pt-lg-2, .py-lg-2 { - padding-top: .5rem !important - } - - .pr-lg-2, .px-lg-2 { - padding-right: .5rem !important - } - - .pb-lg-2, .py-lg-2 { - padding-bottom: .5rem !important - } - - .pl-lg-2, .px-lg-2 { - padding-left: .5rem !important - } - - .p-lg-3 { - padding: 1rem !important - } - - .pt-lg-3, .py-lg-3 { - padding-top: 1rem !important - } - - .pr-lg-3, .px-lg-3 { - padding-right: 1rem !important - } - - .pb-lg-3, .py-lg-3 { - padding-bottom: 1rem !important - } - - .pl-lg-3, .px-lg-3 { - padding-left: 1rem !important - } - - .p-lg-4 { - padding: 1.5rem !important - } - - .pt-lg-4, .py-lg-4 { - padding-top: 1.5rem !important - } - - .pr-lg-4, .px-lg-4 { - padding-right: 1.5rem !important - } - - .pb-lg-4, .py-lg-4 { - padding-bottom: 1.5rem !important - } - - .pl-lg-4, .px-lg-4 { - padding-left: 1.5rem !important - } - - .p-lg-5 { - padding: 3rem !important - } - - .pt-lg-5, .py-lg-5 { - padding-top: 3rem !important - } - - .pr-lg-5, .px-lg-5 { - padding-right: 3rem !important - } - - .pb-lg-5, .py-lg-5 { - padding-bottom: 3rem !important - } - - .pl-lg-5, .px-lg-5 { - padding-left: 3rem !important - } - - .m-lg-auto { - margin: auto !important - } - - .mt-lg-auto, .my-lg-auto { - margin-top: auto !important - } - - .mr-lg-auto, .mx-lg-auto { - margin-right: auto !important - } - - .mb-lg-auto, .my-lg-auto { - margin-bottom: auto !important - } - - .ml-lg-auto, .mx-lg-auto { - margin-left: auto !important - } -} - -@media (min-width: 1200px) { - .m-xl-0 { - margin: 0 !important - } - - .mt-xl-0, .my-xl-0 { - margin-top: 0 !important - } - - .mr-xl-0, .mx-xl-0 { - margin-right: 0 !important - } - - .mb-xl-0, .my-xl-0 { - margin-bottom: 0 !important - } - - .ml-xl-0, .mx-xl-0 { - margin-left: 0 !important - } - - .m-xl-1 { - margin: .25rem !important - } - - .mt-xl-1, .my-xl-1 { - margin-top: .25rem !important - } - - .mr-xl-1, .mx-xl-1 { - margin-right: .25rem !important - } - - .mb-xl-1, .my-xl-1 { - margin-bottom: .25rem !important - } - - .ml-xl-1, .mx-xl-1 { - margin-left: .25rem !important - } - - .m-xl-2 { - margin: .5rem !important - } - - .mt-xl-2, .my-xl-2 { - margin-top: .5rem !important - } - - .mr-xl-2, .mx-xl-2 { - margin-right: .5rem !important - } - - .mb-xl-2, .my-xl-2 { - margin-bottom: .5rem !important - } - - .ml-xl-2, .mx-xl-2 { - margin-left: .5rem !important - } - - .m-xl-3 { - margin: 1rem !important - } - - .mt-xl-3, .my-xl-3 { - margin-top: 1rem !important - } - - .mr-xl-3, .mx-xl-3 { - margin-right: 1rem !important - } - - .mb-xl-3, .my-xl-3 { - margin-bottom: 1rem !important - } - - .ml-xl-3, .mx-xl-3 { - margin-left: 1rem !important - } - - .m-xl-4 { - margin: 1.5rem !important - } - - .mt-xl-4, .my-xl-4 { - margin-top: 1.5rem !important - } - - .mr-xl-4, .mx-xl-4 { - margin-right: 1.5rem !important - } - - .mb-xl-4, .my-xl-4 { - margin-bottom: 1.5rem !important - } - - .ml-xl-4, .mx-xl-4 { - margin-left: 1.5rem !important - } - - .m-xl-5 { - margin: 3rem !important - } - - .mt-xl-5, .my-xl-5 { - margin-top: 3rem !important - } - - .mr-xl-5, .mx-xl-5 { - margin-right: 3rem !important - } - - .mb-xl-5, .my-xl-5 { - margin-bottom: 3rem !important - } - - .ml-xl-5, .mx-xl-5 { - margin-left: 3rem !important - } - - .p-xl-0 { - padding: 0 !important - } - - .pt-xl-0, .py-xl-0 { - padding-top: 0 !important - } - - .pr-xl-0, .px-xl-0 { - padding-right: 0 !important - } - - .pb-xl-0, .py-xl-0 { - padding-bottom: 0 !important - } - - .pl-xl-0, .px-xl-0 { - padding-left: 0 !important - } - - .p-xl-1 { - padding: .25rem !important - } - - .pt-xl-1, .py-xl-1 { - padding-top: .25rem !important - } - - .pr-xl-1, .px-xl-1 { - padding-right: .25rem !important - } - - .pb-xl-1, .py-xl-1 { - padding-bottom: .25rem !important - } - - .pl-xl-1, .px-xl-1 { - padding-left: .25rem !important - } - - .p-xl-2 { - padding: .5rem !important - } - - .pt-xl-2, .py-xl-2 { - padding-top: .5rem !important - } - - .pr-xl-2, .px-xl-2 { - padding-right: .5rem !important - } - - .pb-xl-2, .py-xl-2 { - padding-bottom: .5rem !important - } - - .pl-xl-2, .px-xl-2 { - padding-left: .5rem !important - } - - .p-xl-3 { - padding: 1rem !important - } - - .pt-xl-3, .py-xl-3 { - padding-top: 1rem !important - } - - .pr-xl-3, .px-xl-3 { - padding-right: 1rem !important - } - - .pb-xl-3, .py-xl-3 { - padding-bottom: 1rem !important - } - - .pl-xl-3, .px-xl-3 { - padding-left: 1rem !important - } - - .p-xl-4 { - padding: 1.5rem !important - } - - .pt-xl-4, .py-xl-4 { - padding-top: 1.5rem !important - } - - .pr-xl-4, .px-xl-4 { - padding-right: 1.5rem !important - } - - .pb-xl-4, .py-xl-4 { - padding-bottom: 1.5rem !important - } - - .pl-xl-4, .px-xl-4 { - padding-left: 1.5rem !important - } - - .p-xl-5 { - padding: 3rem !important - } - - .pt-xl-5, .py-xl-5 { - padding-top: 3rem !important - } - - .pr-xl-5, .px-xl-5 { - padding-right: 3rem !important - } - - .pb-xl-5, .py-xl-5 { - padding-bottom: 3rem !important - } - - .pl-xl-5, .px-xl-5 { - padding-left: 3rem !important - } - - .m-xl-auto { - margin: auto !important - } - - .mt-xl-auto, .my-xl-auto { - margin-top: auto !important - } - - .mr-xl-auto, .mx-xl-auto { - margin-right: auto !important - } - - .mb-xl-auto, .my-xl-auto { - margin-bottom: auto !important - } - - .ml-xl-auto, .mx-xl-auto { - margin-left: auto !important - } -} - -.text-justify { - text-align: justify !important -} - -.text-nowrap { - white-space: nowrap !important -} - -.text-truncate { - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap -} - -.text-left { - text-align: left !important -} - -.text-right { - text-align: right !important -} - -.text-center { - text-align: center !important -} - -@media (min-width: 576px) { - .text-sm-left { - text-align: left !important - } - - .text-sm-right { - text-align: right !important - } - - .text-sm-center { - text-align: center !important - } -} - -@media (min-width: 768px) { - .text-md-left { - text-align: left !important - } - - .text-md-right { - text-align: right !important - } - - .text-md-center { - text-align: center !important - } -} - -@media (min-width: 992px) { - .text-lg-left { - text-align: left !important - } - - .text-lg-right { - text-align: right !important - } - - .text-lg-center { - text-align: center !important - } -} - -@media (min-width: 1200px) { - .text-xl-left { - text-align: left !important - } - - .text-xl-right { - text-align: right !important - } - - .text-xl-center { - text-align: center !important - } -} - -.text-lowercase { - text-transform: lowercase !important -} - -.text-uppercase { - text-transform: uppercase !important -} - -.text-capitalize { - text-transform: capitalize !important -} - -.font-weight-light { - font-weight: 300 !important -} - -.font-weight-normal { - font-weight: 400 !important -} - -.font-weight-bold { - font-weight: 700 !important -} - -.font-italic { - font-style: italic !important -} - -.text-white { - color: #fff !important -} - -.text-primary { - color: #009688 !important -} - -a.text-primary:focus, a.text-primary:hover { - color: #00635a !important -} - -.text-secondary { - color: #6c757d !important -} - -a.text-secondary:focus, a.text-secondary:hover { - color: #545b62 !important -} - -.text-success { - color: #4caf50 !important -} - -a.text-success:focus, a.text-success:hover { - color: #3d8b40 !important -} - -.text-info { - color: #03a9f4 !important -} - -a.text-info:focus, a.text-info:hover { - color: #0286c2 !important -} - -.text-warning { - color: #ff5722 !important -} - -a.text-warning:focus, a.text-warning:hover { - color: #ee3900 !important -} - -.text-danger { - color: #f44336 !important -} - -a.text-danger:focus, a.text-danger:hover { - color: #ea1c0d !important -} - -.text-light { - color: #f5f5f5 !important -} - -a.text-light:focus, a.text-light:hover { - color: #dcdcdc !important -} - -.text-dark { - color: #424242 !important -} - -a.text-dark:focus, a.text-dark:hover { - color: #292929 !important -} - -.bmd-help, .text-muted { - color: #6c757d !important -} - -.text-hide { - font: 0/0 a; - color: transparent; - text-shadow: none; - background-color: transparent; - border: 0 -} - -.visible { - visibility: visible !important -} - -.invisible { - visibility: hidden !important -} - -@media print { - *, :after, :before { - text-shadow: none !important; - box-shadow: none !important - } - - a:not(.btn):not(.custom-file-control:before) { - text-decoration: underline - } - - abbr[title]:after { - content: " (" attr(title) ")" - } - - pre { - white-space: pre-wrap !important - } - - blockquote, pre { - border: 1px solid #999; - page-break-inside: avoid - } - - thead { - display: table-header-group - } - - img, tr { - page-break-inside: avoid - } - - h2, h3, p { - orphans: 3; - widows: 3 - } - - h2, h3 { - page-break-after: avoid - } - - @page { - size: a3 - } - - .container, body { - min-width: 992px !important - } - - .navbar { - display: none - } - - .badge { - border: 1px solid #000 - } - - .table { - border-collapse: collapse !important - } - - .table td, .table th { - background-color: #fff !important - } - - .table-bordered td, .table-bordered th { - border: 1px solid #ddd !important - } -} - -body { - font-weight: 400 -} - -a:focus, button:focus { - outline: none -} - -.bmd-layout-canvas { - display: flex; - flex-direction: column; - width: 100%; - height: 100% -} - -.bmd-layout-container { - position: relative; - flex: 1; - display: flex; - flex-direction: column; - width: 100%; - height: 100%; - overflow: hidden -} - -.bmd-layout-header { - z-index: 3; - display: flex; - flex-direction: column; - flex-wrap: nowrap; - flex-shrink: 0; - justify-content: flex-start; - width: 100%; - max-height: 1000px; - transform: translateZ(0) -} - -.bmd-layout-content, .bmd-layout-header { - transition-duration: .2s; - transition-timing-function: cubic-bezier(.4, 0, .2, 1) -} - -.bmd-layout-content { - position: relative; - z-index: 1; - display: inline-block; - flex-grow: 1; - overflow-x: hidden; - overflow-y: auto; - -webkit-overflow-scrolling: touch -} - -.bmd-layout-spacer { - flex-grow: 1 -} - -.bmd-layout-backdrop { - position: absolute; - top: 0; - left: 0; - z-index: 4; - width: 100%; - height: 100%; - visibility: hidden; - background-color: transparent; - transition-property: background-color; - transition-duration: .2s; - transition-timing-function: cubic-bezier(.4, 0, .2, 1) -} - -@supports (pointer-events:auto) { - .bmd-layout-backdrop { - background-color: rgba(0, 0, 0, .5); - opacity: 0; - transition-property: opacity; - visibility: visible; - pointer-events: none - } -} - -.btn, .custom-file-control:before { - position: relative; - margin-bottom: .3125rem; - font-size: .875rem; - text-decoration: none; - text-transform: uppercase; - letter-spacing: 0; - cursor: pointer; - border: 0; - outline: 0; - transition: box-shadow .2s cubic-bezier(.4, 0, 1, 1), background-color .2s cubic-bezier(.4, 0, .2, 1), color .2s cubic-bezier(.4, 0, .2, 1); - will-change: box-shadow, transform; - color: rgba(0, 0, 0, .87); - background-color: transparent; - border-color: #ccc -} - -.active.custom-file-control:before, .btn.active, .btn.focus, .btn:active, .btn:focus, .btn:hover, .custom-file-control:active:before, .custom-file-control:focus:before, .custom-file-control:hover:before, .focus.custom-file-control:before, .open > .btn.dropdown-toggle, .open > .dropdown-toggle.custom-file-control:before { - color: rgba(0, 0, 0, .87); - background-color: hsla(0, 0%, 60%, .2); - border-color: hsla(0, 0%, 60%, .2) -} - -.active.custom-file-control:focus:before, .active.custom-file-control:hover:before, .active.focus.custom-file-control:before, .btn.active.focus, .btn.active:focus, .btn.active:hover, .btn:active.focus, .btn:active:focus, .btn:active:hover, .custom-file-control:active.focus:before, .custom-file-control:active:focus:before, .custom-file-control:active:hover:before, .open > .btn.dropdown-toggle.focus, .open > .btn.dropdown-toggle:focus, .open > .btn.dropdown-toggle:hover, .open > .dropdown-toggle.custom-file-control:focus:before, .open > .dropdown-toggle.custom-file-control:hover:before, .open > .dropdown-toggle.focus.custom-file-control:before { - color: rgba(0, 0, 0, .87); - background-color: hsla(0, 0%, 60%, .4); - border-color: hsla(0, 0%, 60%, .4) -} - -.open > .btn.dropdown-toggle.bmd-btn-icon, .open > .dropdown-toggle.bmd-btn-icon.custom-file-control:before { - color: inherit; - background-color: transparent -} - -.open > .btn.dropdown-toggle.bmd-btn-icon:hover, .open > .dropdown-toggle.bmd-btn-icon.custom-file-control:hover:before { - background-color: hsla(0, 0%, 60%, .2) -} - -.btn.disabled.focus, .btn.disabled:focus, .btn.disabled:hover, .btn:disabled.focus, .btn:disabled:focus, .btn:disabled:hover, .custom-file-control:disabled.focus:before, .custom-file-control:disabled:focus:before, .custom-file-control:disabled:hover:before, .disabled.custom-file-control:focus:before, .disabled.custom-file-control:hover:before, .disabled.focus.custom-file-control:before { - background-color: transparent; - border-color: #ccc -} - -.bg-inverse .btn, .bg-inverse .custom-file-control:before { - color: rgba(0, 0, 0, .87); - background-color: transparent; - border-color: #ccc -} - -.bg-inverse .active.custom-file-control:before, .bg-inverse .btn.active, .bg-inverse .btn.focus, .bg-inverse .btn:active, .bg-inverse .btn:focus, .bg-inverse .btn:hover, .bg-inverse .custom-file-control:active:before, .bg-inverse .custom-file-control:focus:before, .bg-inverse .custom-file-control:hover:before, .bg-inverse .focus.custom-file-control:before, .open > .bg-inverse .btn.dropdown-toggle, .open > .bg-inverse .dropdown-toggle.custom-file-control:before { - color: rgba(0, 0, 0, .87); - background-color: hsla(0, 0%, 80%, .15); - border-color: hsla(0, 0%, 80%, .15) -} - -.bg-inverse .active.custom-file-control:focus:before, .bg-inverse .active.custom-file-control:hover:before, .bg-inverse .active.focus.custom-file-control:before, .bg-inverse .btn.active.focus, .bg-inverse .btn.active:focus, .bg-inverse .btn.active:hover, .bg-inverse .btn:active.focus, .bg-inverse .btn:active:focus, .bg-inverse .btn:active:hover, .bg-inverse .custom-file-control:active.focus:before, .bg-inverse .custom-file-control:active:focus:before, .bg-inverse .custom-file-control:active:hover:before, .open > .bg-inverse .btn.dropdown-toggle.focus, .open > .bg-inverse .btn.dropdown-toggle:focus, .open > .bg-inverse .btn.dropdown-toggle:hover, .open > .bg-inverse .dropdown-toggle.custom-file-control:focus:before, .open > .bg-inverse .dropdown-toggle.custom-file-control:hover:before, .open > .bg-inverse .dropdown-toggle.focus.custom-file-control:before { - color: rgba(0, 0, 0, .87); - background-color: hsla(0, 0%, 80%, .25); - border-color: hsla(0, 0%, 80%, .25) -} - -.open > .bg-inverse .btn.dropdown-toggle.bmd-btn-icon, .open > .bg-inverse .dropdown-toggle.bmd-btn-icon.custom-file-control:before { - color: inherit; - background-color: transparent -} - -.open > .bg-inverse .btn.dropdown-toggle.bmd-btn-icon:hover, .open > .bg-inverse .dropdown-toggle.bmd-btn-icon.custom-file-control:hover:before { - background-color: hsla(0, 0%, 80%, .15) -} - -.bg-inverse .btn.disabled.focus, .bg-inverse .btn.disabled:focus, .bg-inverse .btn.disabled:hover, .bg-inverse .btn:disabled.focus, .bg-inverse .btn:disabled:focus, .bg-inverse .btn:disabled:hover, .bg-inverse .custom-file-control:disabled.focus:before, .bg-inverse .custom-file-control:disabled:focus:before, .bg-inverse .custom-file-control:disabled:hover:before, .bg-inverse .disabled.custom-file-control:focus:before, .bg-inverse .disabled.custom-file-control:hover:before, .bg-inverse .disabled.focus.custom-file-control:before { - background-color: transparent; - border-color: #ccc -} - -.btn-link.custom-file-control:before, .btn.btn-link { - background-color: transparent -} - -.btn-primary.custom-file-control:before, .btn.btn-primary { - color: #009688; - background-color: transparent; - border-color: #ccc -} - -.btn-primary.active.custom-file-control:before, .btn-primary.custom-file-control:active:before, .btn-primary.custom-file-control:focus:before, .btn-primary.custom-file-control:hover:before, .btn-primary.focus.custom-file-control:before, .btn.btn-primary.active, .btn.btn-primary.focus, .btn.btn-primary:active, .btn.btn-primary:focus, .btn.btn-primary:hover, .open > .btn-primary.dropdown-toggle.custom-file-control:before, .open > .btn.btn-primary.dropdown-toggle { - color: #009688; - background-color: hsla(0, 0%, 60%, .2); - border-color: hsla(0, 0%, 60%, .2) -} - -.btn-primary.active.custom-file-control:focus:before, .btn-primary.active.custom-file-control:hover:before, .btn-primary.active.focus.custom-file-control:before, .btn-primary.custom-file-control:active.focus:before, .btn-primary.custom-file-control:active:focus:before, .btn-primary.custom-file-control:active:hover:before, .btn.btn-primary.active.focus, .btn.btn-primary.active:focus, .btn.btn-primary.active:hover, .btn.btn-primary:active.focus, .btn.btn-primary:active:focus, .btn.btn-primary:active:hover, .open > .btn-primary.dropdown-toggle.custom-file-control:focus:before, .open > .btn-primary.dropdown-toggle.custom-file-control:hover:before, .open > .btn-primary.dropdown-toggle.focus.custom-file-control:before, .open > .btn.btn-primary.dropdown-toggle.focus, .open > .btn.btn-primary.dropdown-toggle:focus, .open > .btn.btn-primary.dropdown-toggle:hover { - color: #009688; - background-color: hsla(0, 0%, 60%, .4); - border-color: hsla(0, 0%, 60%, .4) -} - -.open > .btn-primary.dropdown-toggle.bmd-btn-icon.custom-file-control:before, .open > .btn.btn-primary.dropdown-toggle.bmd-btn-icon { - color: inherit; - background-color: transparent -} - -.open > .btn-primary.dropdown-toggle.bmd-btn-icon.custom-file-control:hover:before, .open > .btn.btn-primary.dropdown-toggle.bmd-btn-icon:hover { - background-color: hsla(0, 0%, 60%, .2) -} - -.btn-primary.custom-file-control:disabled.focus:before, .btn-primary.custom-file-control:disabled:focus:before, .btn-primary.custom-file-control:disabled:hover:before, .btn-primary.disabled.custom-file-control:focus:before, .btn-primary.disabled.custom-file-control:hover:before, .btn-primary.disabled.focus.custom-file-control:before, .btn.btn-primary.disabled.focus, .btn.btn-primary.disabled:focus, .btn.btn-primary.disabled:hover, .btn.btn-primary:disabled.focus, .btn.btn-primary:disabled:focus, .btn.btn-primary:disabled:hover { - background-color: transparent; - border-color: #ccc -} - -.bg-inverse .btn-primary.custom-file-control:before, .bg-inverse .btn.btn-primary { - color: #009688; - background-color: transparent; - border-color: #ccc -} - -.bg-inverse .btn-primary.active.custom-file-control:before, .bg-inverse .btn-primary.custom-file-control:active:before, .bg-inverse .btn-primary.custom-file-control:focus:before, .bg-inverse .btn-primary.custom-file-control:hover:before, .bg-inverse .btn-primary.focus.custom-file-control:before, .bg-inverse .btn.btn-primary.active, .bg-inverse .btn.btn-primary.focus, .bg-inverse .btn.btn-primary:active, .bg-inverse .btn.btn-primary:focus, .bg-inverse .btn.btn-primary:hover, .open > .bg-inverse .btn-primary.dropdown-toggle.custom-file-control:before, .open > .bg-inverse .btn.btn-primary.dropdown-toggle { - color: #009688; - background-color: hsla(0, 0%, 80%, .15); - border-color: hsla(0, 0%, 80%, .15) -} - -.bg-inverse .btn-primary.active.custom-file-control:focus:before, .bg-inverse .btn-primary.active.custom-file-control:hover:before, .bg-inverse .btn-primary.active.focus.custom-file-control:before, .bg-inverse .btn-primary.custom-file-control:active.focus:before, .bg-inverse .btn-primary.custom-file-control:active:focus:before, .bg-inverse .btn-primary.custom-file-control:active:hover:before, .bg-inverse .btn.btn-primary.active.focus, .bg-inverse .btn.btn-primary.active:focus, .bg-inverse .btn.btn-primary.active:hover, .bg-inverse .btn.btn-primary:active.focus, .bg-inverse .btn.btn-primary:active:focus, .bg-inverse .btn.btn-primary:active:hover, .open > .bg-inverse .btn-primary.dropdown-toggle.custom-file-control:focus:before, .open > .bg-inverse .btn-primary.dropdown-toggle.custom-file-control:hover:before, .open > .bg-inverse .btn-primary.dropdown-toggle.focus.custom-file-control:before, .open > .bg-inverse .btn.btn-primary.dropdown-toggle.focus, .open > .bg-inverse .btn.btn-primary.dropdown-toggle:focus, .open > .bg-inverse .btn.btn-primary.dropdown-toggle:hover { - color: #009688; - background-color: hsla(0, 0%, 80%, .25); - border-color: hsla(0, 0%, 80%, .25) -} - -.open > .bg-inverse .btn-primary.dropdown-toggle.bmd-btn-icon.custom-file-control:before, .open > .bg-inverse .btn.btn-primary.dropdown-toggle.bmd-btn-icon { - color: inherit; - background-color: transparent -} - -.open > .bg-inverse .btn-primary.dropdown-toggle.bmd-btn-icon.custom-file-control:hover:before, .open > .bg-inverse .btn.btn-primary.dropdown-toggle.bmd-btn-icon:hover { - background-color: hsla(0, 0%, 80%, .15) -} - -.bg-inverse .btn-primary.custom-file-control:disabled.focus:before, .bg-inverse .btn-primary.custom-file-control:disabled:focus:before, .bg-inverse .btn-primary.custom-file-control:disabled:hover:before, .bg-inverse .btn-primary.disabled.custom-file-control:focus:before, .bg-inverse .btn-primary.disabled.custom-file-control:hover:before, .bg-inverse .btn-primary.disabled.focus.custom-file-control:before, .bg-inverse .btn.btn-primary.disabled.focus, .bg-inverse .btn.btn-primary.disabled:focus, .bg-inverse .btn.btn-primary.disabled:hover, .bg-inverse .btn.btn-primary:disabled.focus, .bg-inverse .btn.btn-primary:disabled:focus, .bg-inverse .btn.btn-primary:disabled:hover { - background-color: transparent; - border-color: #ccc -} - -.btn-primary.btn-link.custom-file-control:before, .btn.btn-primary.btn-link { - background-color: transparent -} - -.btn-secondary.custom-file-control:before, .btn.btn-secondary { - color: #6c757d; - background-color: transparent; - border-color: #ccc -} - -.btn-secondary.active.custom-file-control:before, .btn-secondary.custom-file-control:active:before, .btn-secondary.custom-file-control:focus:before, .btn-secondary.custom-file-control:hover:before, .btn-secondary.focus.custom-file-control:before, .btn.btn-secondary.active, .btn.btn-secondary.focus, .btn.btn-secondary:active, .btn.btn-secondary:focus, .btn.btn-secondary:hover, .open > .btn-secondary.dropdown-toggle.custom-file-control:before, .open > .btn.btn-secondary.dropdown-toggle { - color: #6c757d; - background-color: hsla(0, 0%, 60%, .2); - border-color: hsla(0, 0%, 60%, .2) -} - -.btn-secondary.active.custom-file-control:focus:before, .btn-secondary.active.custom-file-control:hover:before, .btn-secondary.active.focus.custom-file-control:before, .btn-secondary.custom-file-control:active.focus:before, .btn-secondary.custom-file-control:active:focus:before, .btn-secondary.custom-file-control:active:hover:before, .btn.btn-secondary.active.focus, .btn.btn-secondary.active:focus, .btn.btn-secondary.active:hover, .btn.btn-secondary:active.focus, .btn.btn-secondary:active:focus, .btn.btn-secondary:active:hover, .open > .btn-secondary.dropdown-toggle.custom-file-control:focus:before, .open > .btn-secondary.dropdown-toggle.custom-file-control:hover:before, .open > .btn-secondary.dropdown-toggle.focus.custom-file-control:before, .open > .btn.btn-secondary.dropdown-toggle.focus, .open > .btn.btn-secondary.dropdown-toggle:focus, .open > .btn.btn-secondary.dropdown-toggle:hover { - color: #6c757d; - background-color: hsla(0, 0%, 60%, .4); - border-color: hsla(0, 0%, 60%, .4) -} - -.open > .btn-secondary.dropdown-toggle.bmd-btn-icon.custom-file-control:before, .open > .btn.btn-secondary.dropdown-toggle.bmd-btn-icon { - color: inherit; - background-color: transparent -} - -.open > .btn-secondary.dropdown-toggle.bmd-btn-icon.custom-file-control:hover:before, .open > .btn.btn-secondary.dropdown-toggle.bmd-btn-icon:hover { - background-color: hsla(0, 0%, 60%, .2) -} - -.btn-secondary.custom-file-control:disabled.focus:before, .btn-secondary.custom-file-control:disabled:focus:before, .btn-secondary.custom-file-control:disabled:hover:before, .btn-secondary.disabled.custom-file-control:focus:before, .btn-secondary.disabled.custom-file-control:hover:before, .btn-secondary.disabled.focus.custom-file-control:before, .btn.btn-secondary.disabled.focus, .btn.btn-secondary.disabled:focus, .btn.btn-secondary.disabled:hover, .btn.btn-secondary:disabled.focus, .btn.btn-secondary:disabled:focus, .btn.btn-secondary:disabled:hover { - background-color: transparent; - border-color: #ccc -} - -.bg-inverse .btn-secondary.custom-file-control:before, .bg-inverse .btn.btn-secondary { - color: #6c757d; - background-color: transparent; - border-color: #ccc -} - -.bg-inverse .btn-secondary.active.custom-file-control:before, .bg-inverse .btn-secondary.custom-file-control:active:before, .bg-inverse .btn-secondary.custom-file-control:focus:before, .bg-inverse .btn-secondary.custom-file-control:hover:before, .bg-inverse .btn-secondary.focus.custom-file-control:before, .bg-inverse .btn.btn-secondary.active, .bg-inverse .btn.btn-secondary.focus, .bg-inverse .btn.btn-secondary:active, .bg-inverse .btn.btn-secondary:focus, .bg-inverse .btn.btn-secondary:hover, .open > .bg-inverse .btn-secondary.dropdown-toggle.custom-file-control:before, .open > .bg-inverse .btn.btn-secondary.dropdown-toggle { - color: #6c757d; - background-color: hsla(0, 0%, 80%, .15); - border-color: hsla(0, 0%, 80%, .15) -} - -.bg-inverse .btn-secondary.active.custom-file-control:focus:before, .bg-inverse .btn-secondary.active.custom-file-control:hover:before, .bg-inverse .btn-secondary.active.focus.custom-file-control:before, .bg-inverse .btn-secondary.custom-file-control:active.focus:before, .bg-inverse .btn-secondary.custom-file-control:active:focus:before, .bg-inverse .btn-secondary.custom-file-control:active:hover:before, .bg-inverse .btn.btn-secondary.active.focus, .bg-inverse .btn.btn-secondary.active:focus, .bg-inverse .btn.btn-secondary.active:hover, .bg-inverse .btn.btn-secondary:active.focus, .bg-inverse .btn.btn-secondary:active:focus, .bg-inverse .btn.btn-secondary:active:hover, .open > .bg-inverse .btn-secondary.dropdown-toggle.custom-file-control:focus:before, .open > .bg-inverse .btn-secondary.dropdown-toggle.custom-file-control:hover:before, .open > .bg-inverse .btn-secondary.dropdown-toggle.focus.custom-file-control:before, .open > .bg-inverse .btn.btn-secondary.dropdown-toggle.focus, .open > .bg-inverse .btn.btn-secondary.dropdown-toggle:focus, .open > .bg-inverse .btn.btn-secondary.dropdown-toggle:hover { - color: #6c757d; - background-color: hsla(0, 0%, 80%, .25); - border-color: hsla(0, 0%, 80%, .25) -} - -.open > .bg-inverse .btn-secondary.dropdown-toggle.bmd-btn-icon.custom-file-control:before, .open > .bg-inverse .btn.btn-secondary.dropdown-toggle.bmd-btn-icon { - color: inherit; - background-color: transparent -} - -.open > .bg-inverse .btn-secondary.dropdown-toggle.bmd-btn-icon.custom-file-control:hover:before, .open > .bg-inverse .btn.btn-secondary.dropdown-toggle.bmd-btn-icon:hover { - background-color: hsla(0, 0%, 80%, .15) -} - -.bg-inverse .btn-secondary.custom-file-control:disabled.focus:before, .bg-inverse .btn-secondary.custom-file-control:disabled:focus:before, .bg-inverse .btn-secondary.custom-file-control:disabled:hover:before, .bg-inverse .btn-secondary.disabled.custom-file-control:focus:before, .bg-inverse .btn-secondary.disabled.custom-file-control:hover:before, .bg-inverse .btn-secondary.disabled.focus.custom-file-control:before, .bg-inverse .btn.btn-secondary.disabled.focus, .bg-inverse .btn.btn-secondary.disabled:focus, .bg-inverse .btn.btn-secondary.disabled:hover, .bg-inverse .btn.btn-secondary:disabled.focus, .bg-inverse .btn.btn-secondary:disabled:focus, .bg-inverse .btn.btn-secondary:disabled:hover { - background-color: transparent; - border-color: #ccc -} - -.btn-secondary.btn-link.custom-file-control:before, .btn.btn-secondary.btn-link { - background-color: transparent -} - -.btn-success.custom-file-control:before, .btn.btn-success { - color: #4caf50; - background-color: transparent; - border-color: #ccc -} - -.btn-success.active.custom-file-control:before, .btn-success.custom-file-control:active:before, .btn-success.custom-file-control:focus:before, .btn-success.custom-file-control:hover:before, .btn-success.focus.custom-file-control:before, .btn.btn-success.active, .btn.btn-success.focus, .btn.btn-success:active, .btn.btn-success:focus, .btn.btn-success:hover, .open > .btn-success.dropdown-toggle.custom-file-control:before, .open > .btn.btn-success.dropdown-toggle { - color: #4caf50; - background-color: hsla(0, 0%, 60%, .2); - border-color: hsla(0, 0%, 60%, .2) -} - -.btn-success.active.custom-file-control:focus:before, .btn-success.active.custom-file-control:hover:before, .btn-success.active.focus.custom-file-control:before, .btn-success.custom-file-control:active.focus:before, .btn-success.custom-file-control:active:focus:before, .btn-success.custom-file-control:active:hover:before, .btn.btn-success.active.focus, .btn.btn-success.active:focus, .btn.btn-success.active:hover, .btn.btn-success:active.focus, .btn.btn-success:active:focus, .btn.btn-success:active:hover, .open > .btn-success.dropdown-toggle.custom-file-control:focus:before, .open > .btn-success.dropdown-toggle.custom-file-control:hover:before, .open > .btn-success.dropdown-toggle.focus.custom-file-control:before, .open > .btn.btn-success.dropdown-toggle.focus, .open > .btn.btn-success.dropdown-toggle:focus, .open > .btn.btn-success.dropdown-toggle:hover { - color: #4caf50; - background-color: hsla(0, 0%, 60%, .4); - border-color: hsla(0, 0%, 60%, .4) -} - -.open > .btn-success.dropdown-toggle.bmd-btn-icon.custom-file-control:before, .open > .btn.btn-success.dropdown-toggle.bmd-btn-icon { - color: inherit; - background-color: transparent -} - -.open > .btn-success.dropdown-toggle.bmd-btn-icon.custom-file-control:hover:before, .open > .btn.btn-success.dropdown-toggle.bmd-btn-icon:hover { - background-color: hsla(0, 0%, 60%, .2) -} - -.btn-success.custom-file-control:disabled.focus:before, .btn-success.custom-file-control:disabled:focus:before, .btn-success.custom-file-control:disabled:hover:before, .btn-success.disabled.custom-file-control:focus:before, .btn-success.disabled.custom-file-control:hover:before, .btn-success.disabled.focus.custom-file-control:before, .btn.btn-success.disabled.focus, .btn.btn-success.disabled:focus, .btn.btn-success.disabled:hover, .btn.btn-success:disabled.focus, .btn.btn-success:disabled:focus, .btn.btn-success:disabled:hover { - background-color: transparent; - border-color: #ccc -} - -.bg-inverse .btn-success.custom-file-control:before, .bg-inverse .btn.btn-success { - color: #4caf50; - background-color: transparent; - border-color: #ccc -} - -.bg-inverse .btn-success.active.custom-file-control:before, .bg-inverse .btn-success.custom-file-control:active:before, .bg-inverse .btn-success.custom-file-control:focus:before, .bg-inverse .btn-success.custom-file-control:hover:before, .bg-inverse .btn-success.focus.custom-file-control:before, .bg-inverse .btn.btn-success.active, .bg-inverse .btn.btn-success.focus, .bg-inverse .btn.btn-success:active, .bg-inverse .btn.btn-success:focus, .bg-inverse .btn.btn-success:hover, .open > .bg-inverse .btn-success.dropdown-toggle.custom-file-control:before, .open > .bg-inverse .btn.btn-success.dropdown-toggle { - color: #4caf50; - background-color: hsla(0, 0%, 80%, .15); - border-color: hsla(0, 0%, 80%, .15) -} - -.bg-inverse .btn-success.active.custom-file-control:focus:before, .bg-inverse .btn-success.active.custom-file-control:hover:before, .bg-inverse .btn-success.active.focus.custom-file-control:before, .bg-inverse .btn-success.custom-file-control:active.focus:before, .bg-inverse .btn-success.custom-file-control:active:focus:before, .bg-inverse .btn-success.custom-file-control:active:hover:before, .bg-inverse .btn.btn-success.active.focus, .bg-inverse .btn.btn-success.active:focus, .bg-inverse .btn.btn-success.active:hover, .bg-inverse .btn.btn-success:active.focus, .bg-inverse .btn.btn-success:active:focus, .bg-inverse .btn.btn-success:active:hover, .open > .bg-inverse .btn-success.dropdown-toggle.custom-file-control:focus:before, .open > .bg-inverse .btn-success.dropdown-toggle.custom-file-control:hover:before, .open > .bg-inverse .btn-success.dropdown-toggle.focus.custom-file-control:before, .open > .bg-inverse .btn.btn-success.dropdown-toggle.focus, .open > .bg-inverse .btn.btn-success.dropdown-toggle:focus, .open > .bg-inverse .btn.btn-success.dropdown-toggle:hover { - color: #4caf50; - background-color: hsla(0, 0%, 80%, .25); - border-color: hsla(0, 0%, 80%, .25) -} - -.open > .bg-inverse .btn-success.dropdown-toggle.bmd-btn-icon.custom-file-control:before, .open > .bg-inverse .btn.btn-success.dropdown-toggle.bmd-btn-icon { - color: inherit; - background-color: transparent -} - -.open > .bg-inverse .btn-success.dropdown-toggle.bmd-btn-icon.custom-file-control:hover:before, .open > .bg-inverse .btn.btn-success.dropdown-toggle.bmd-btn-icon:hover { - background-color: hsla(0, 0%, 80%, .15) -} - -.bg-inverse .btn-success.custom-file-control:disabled.focus:before, .bg-inverse .btn-success.custom-file-control:disabled:focus:before, .bg-inverse .btn-success.custom-file-control:disabled:hover:before, .bg-inverse .btn-success.disabled.custom-file-control:focus:before, .bg-inverse .btn-success.disabled.custom-file-control:hover:before, .bg-inverse .btn-success.disabled.focus.custom-file-control:before, .bg-inverse .btn.btn-success.disabled.focus, .bg-inverse .btn.btn-success.disabled:focus, .bg-inverse .btn.btn-success.disabled:hover, .bg-inverse .btn.btn-success:disabled.focus, .bg-inverse .btn.btn-success:disabled:focus, .bg-inverse .btn.btn-success:disabled:hover { - background-color: transparent; - border-color: #ccc -} - -.btn-success.btn-link.custom-file-control:before, .btn.btn-success.btn-link { - background-color: transparent -} - -.btn-info.custom-file-control:before, .btn.btn-info { - color: #03a9f4; - background-color: transparent; - border-color: #ccc -} - -.btn-info.active.custom-file-control:before, .btn-info.custom-file-control:active:before, .btn-info.custom-file-control:focus:before, .btn-info.custom-file-control:hover:before, .btn-info.focus.custom-file-control:before, .btn.btn-info.active, .btn.btn-info.focus, .btn.btn-info:active, .btn.btn-info:focus, .btn.btn-info:hover, .open > .btn-info.dropdown-toggle.custom-file-control:before, .open > .btn.btn-info.dropdown-toggle { - color: #03a9f4; - background-color: hsla(0, 0%, 60%, .2); - border-color: hsla(0, 0%, 60%, .2) -} - -.btn-info.active.custom-file-control:focus:before, .btn-info.active.custom-file-control:hover:before, .btn-info.active.focus.custom-file-control:before, .btn-info.custom-file-control:active.focus:before, .btn-info.custom-file-control:active:focus:before, .btn-info.custom-file-control:active:hover:before, .btn.btn-info.active.focus, .btn.btn-info.active:focus, .btn.btn-info.active:hover, .btn.btn-info:active.focus, .btn.btn-info:active:focus, .btn.btn-info:active:hover, .open > .btn-info.dropdown-toggle.custom-file-control:focus:before, .open > .btn-info.dropdown-toggle.custom-file-control:hover:before, .open > .btn-info.dropdown-toggle.focus.custom-file-control:before, .open > .btn.btn-info.dropdown-toggle.focus, .open > .btn.btn-info.dropdown-toggle:focus, .open > .btn.btn-info.dropdown-toggle:hover { - color: #03a9f4; - background-color: hsla(0, 0%, 60%, .4); - border-color: hsla(0, 0%, 60%, .4) -} - -.open > .btn-info.dropdown-toggle.bmd-btn-icon.custom-file-control:before, .open > .btn.btn-info.dropdown-toggle.bmd-btn-icon { - color: inherit; - background-color: transparent -} - -.open > .btn-info.dropdown-toggle.bmd-btn-icon.custom-file-control:hover:before, .open > .btn.btn-info.dropdown-toggle.bmd-btn-icon:hover { - background-color: hsla(0, 0%, 60%, .2) -} - -.btn-info.custom-file-control:disabled.focus:before, .btn-info.custom-file-control:disabled:focus:before, .btn-info.custom-file-control:disabled:hover:before, .btn-info.disabled.custom-file-control:focus:before, .btn-info.disabled.custom-file-control:hover:before, .btn-info.disabled.focus.custom-file-control:before, .btn.btn-info.disabled.focus, .btn.btn-info.disabled:focus, .btn.btn-info.disabled:hover, .btn.btn-info:disabled.focus, .btn.btn-info:disabled:focus, .btn.btn-info:disabled:hover { - background-color: transparent; - border-color: #ccc -} - -.bg-inverse .btn-info.custom-file-control:before, .bg-inverse .btn.btn-info { - color: #03a9f4; - background-color: transparent; - border-color: #ccc -} - -.bg-inverse .btn-info.active.custom-file-control:before, .bg-inverse .btn-info.custom-file-control:active:before, .bg-inverse .btn-info.custom-file-control:focus:before, .bg-inverse .btn-info.custom-file-control:hover:before, .bg-inverse .btn-info.focus.custom-file-control:before, .bg-inverse .btn.btn-info.active, .bg-inverse .btn.btn-info.focus, .bg-inverse .btn.btn-info:active, .bg-inverse .btn.btn-info:focus, .bg-inverse .btn.btn-info:hover, .open > .bg-inverse .btn-info.dropdown-toggle.custom-file-control:before, .open > .bg-inverse .btn.btn-info.dropdown-toggle { - color: #03a9f4; - background-color: hsla(0, 0%, 80%, .15); - border-color: hsla(0, 0%, 80%, .15) -} - -.bg-inverse .btn-info.active.custom-file-control:focus:before, .bg-inverse .btn-info.active.custom-file-control:hover:before, .bg-inverse .btn-info.active.focus.custom-file-control:before, .bg-inverse .btn-info.custom-file-control:active.focus:before, .bg-inverse .btn-info.custom-file-control:active:focus:before, .bg-inverse .btn-info.custom-file-control:active:hover:before, .bg-inverse .btn.btn-info.active.focus, .bg-inverse .btn.btn-info.active:focus, .bg-inverse .btn.btn-info.active:hover, .bg-inverse .btn.btn-info:active.focus, .bg-inverse .btn.btn-info:active:focus, .bg-inverse .btn.btn-info:active:hover, .open > .bg-inverse .btn-info.dropdown-toggle.custom-file-control:focus:before, .open > .bg-inverse .btn-info.dropdown-toggle.custom-file-control:hover:before, .open > .bg-inverse .btn-info.dropdown-toggle.focus.custom-file-control:before, .open > .bg-inverse .btn.btn-info.dropdown-toggle.focus, .open > .bg-inverse .btn.btn-info.dropdown-toggle:focus, .open > .bg-inverse .btn.btn-info.dropdown-toggle:hover { - color: #03a9f4; - background-color: hsla(0, 0%, 80%, .25); - border-color: hsla(0, 0%, 80%, .25) -} - -.open > .bg-inverse .btn-info.dropdown-toggle.bmd-btn-icon.custom-file-control:before, .open > .bg-inverse .btn.btn-info.dropdown-toggle.bmd-btn-icon { - color: inherit; - background-color: transparent -} - -.open > .bg-inverse .btn-info.dropdown-toggle.bmd-btn-icon.custom-file-control:hover:before, .open > .bg-inverse .btn.btn-info.dropdown-toggle.bmd-btn-icon:hover { - background-color: hsla(0, 0%, 80%, .15) -} - -.bg-inverse .btn-info.custom-file-control:disabled.focus:before, .bg-inverse .btn-info.custom-file-control:disabled:focus:before, .bg-inverse .btn-info.custom-file-control:disabled:hover:before, .bg-inverse .btn-info.disabled.custom-file-control:focus:before, .bg-inverse .btn-info.disabled.custom-file-control:hover:before, .bg-inverse .btn-info.disabled.focus.custom-file-control:before, .bg-inverse .btn.btn-info.disabled.focus, .bg-inverse .btn.btn-info.disabled:focus, .bg-inverse .btn.btn-info.disabled:hover, .bg-inverse .btn.btn-info:disabled.focus, .bg-inverse .btn.btn-info:disabled:focus, .bg-inverse .btn.btn-info:disabled:hover { - background-color: transparent; - border-color: #ccc -} - -.btn-info.btn-link.custom-file-control:before, .btn.btn-info.btn-link { - background-color: transparent -} - -.btn-warning.custom-file-control:before, .btn.btn-warning { - color: #ff5722; - background-color: transparent; - border-color: #ccc -} - -.btn-warning.active.custom-file-control:before, .btn-warning.custom-file-control:active:before, .btn-warning.custom-file-control:focus:before, .btn-warning.custom-file-control:hover:before, .btn-warning.focus.custom-file-control:before, .btn.btn-warning.active, .btn.btn-warning.focus, .btn.btn-warning:active, .btn.btn-warning:focus, .btn.btn-warning:hover, .open > .btn-warning.dropdown-toggle.custom-file-control:before, .open > .btn.btn-warning.dropdown-toggle { - color: #ff5722; - background-color: hsla(0, 0%, 60%, .2); - border-color: hsla(0, 0%, 60%, .2) -} - -.btn-warning.active.custom-file-control:focus:before, .btn-warning.active.custom-file-control:hover:before, .btn-warning.active.focus.custom-file-control:before, .btn-warning.custom-file-control:active.focus:before, .btn-warning.custom-file-control:active:focus:before, .btn-warning.custom-file-control:active:hover:before, .btn.btn-warning.active.focus, .btn.btn-warning.active:focus, .btn.btn-warning.active:hover, .btn.btn-warning:active.focus, .btn.btn-warning:active:focus, .btn.btn-warning:active:hover, .open > .btn-warning.dropdown-toggle.custom-file-control:focus:before, .open > .btn-warning.dropdown-toggle.custom-file-control:hover:before, .open > .btn-warning.dropdown-toggle.focus.custom-file-control:before, .open > .btn.btn-warning.dropdown-toggle.focus, .open > .btn.btn-warning.dropdown-toggle:focus, .open > .btn.btn-warning.dropdown-toggle:hover { - color: #ff5722; - background-color: hsla(0, 0%, 60%, .4); - border-color: hsla(0, 0%, 60%, .4) -} - -.open > .btn-warning.dropdown-toggle.bmd-btn-icon.custom-file-control:before, .open > .btn.btn-warning.dropdown-toggle.bmd-btn-icon { - color: inherit; - background-color: transparent -} - -.open > .btn-warning.dropdown-toggle.bmd-btn-icon.custom-file-control:hover:before, .open > .btn.btn-warning.dropdown-toggle.bmd-btn-icon:hover { - background-color: hsla(0, 0%, 60%, .2) -} - -.btn-warning.custom-file-control:disabled.focus:before, .btn-warning.custom-file-control:disabled:focus:before, .btn-warning.custom-file-control:disabled:hover:before, .btn-warning.disabled.custom-file-control:focus:before, .btn-warning.disabled.custom-file-control:hover:before, .btn-warning.disabled.focus.custom-file-control:before, .btn.btn-warning.disabled.focus, .btn.btn-warning.disabled:focus, .btn.btn-warning.disabled:hover, .btn.btn-warning:disabled.focus, .btn.btn-warning:disabled:focus, .btn.btn-warning:disabled:hover { - background-color: transparent; - border-color: #ccc -} - -.bg-inverse .btn-warning.custom-file-control:before, .bg-inverse .btn.btn-warning { - color: #ff5722; - background-color: transparent; - border-color: #ccc -} - -.bg-inverse .btn-warning.active.custom-file-control:before, .bg-inverse .btn-warning.custom-file-control:active:before, .bg-inverse .btn-warning.custom-file-control:focus:before, .bg-inverse .btn-warning.custom-file-control:hover:before, .bg-inverse .btn-warning.focus.custom-file-control:before, .bg-inverse .btn.btn-warning.active, .bg-inverse .btn.btn-warning.focus, .bg-inverse .btn.btn-warning:active, .bg-inverse .btn.btn-warning:focus, .bg-inverse .btn.btn-warning:hover, .open > .bg-inverse .btn-warning.dropdown-toggle.custom-file-control:before, .open > .bg-inverse .btn.btn-warning.dropdown-toggle { - color: #ff5722; - background-color: hsla(0, 0%, 80%, .15); - border-color: hsla(0, 0%, 80%, .15) -} - -.bg-inverse .btn-warning.active.custom-file-control:focus:before, .bg-inverse .btn-warning.active.custom-file-control:hover:before, .bg-inverse .btn-warning.active.focus.custom-file-control:before, .bg-inverse .btn-warning.custom-file-control:active.focus:before, .bg-inverse .btn-warning.custom-file-control:active:focus:before, .bg-inverse .btn-warning.custom-file-control:active:hover:before, .bg-inverse .btn.btn-warning.active.focus, .bg-inverse .btn.btn-warning.active:focus, .bg-inverse .btn.btn-warning.active:hover, .bg-inverse .btn.btn-warning:active.focus, .bg-inverse .btn.btn-warning:active:focus, .bg-inverse .btn.btn-warning:active:hover, .open > .bg-inverse .btn-warning.dropdown-toggle.custom-file-control:focus:before, .open > .bg-inverse .btn-warning.dropdown-toggle.custom-file-control:hover:before, .open > .bg-inverse .btn-warning.dropdown-toggle.focus.custom-file-control:before, .open > .bg-inverse .btn.btn-warning.dropdown-toggle.focus, .open > .bg-inverse .btn.btn-warning.dropdown-toggle:focus, .open > .bg-inverse .btn.btn-warning.dropdown-toggle:hover { - color: #ff5722; - background-color: hsla(0, 0%, 80%, .25); - border-color: hsla(0, 0%, 80%, .25) -} - -.open > .bg-inverse .btn-warning.dropdown-toggle.bmd-btn-icon.custom-file-control:before, .open > .bg-inverse .btn.btn-warning.dropdown-toggle.bmd-btn-icon { - color: inherit; - background-color: transparent -} - -.open > .bg-inverse .btn-warning.dropdown-toggle.bmd-btn-icon.custom-file-control:hover:before, .open > .bg-inverse .btn.btn-warning.dropdown-toggle.bmd-btn-icon:hover { - background-color: hsla(0, 0%, 80%, .15) -} - -.bg-inverse .btn-warning.custom-file-control:disabled.focus:before, .bg-inverse .btn-warning.custom-file-control:disabled:focus:before, .bg-inverse .btn-warning.custom-file-control:disabled:hover:before, .bg-inverse .btn-warning.disabled.custom-file-control:focus:before, .bg-inverse .btn-warning.disabled.custom-file-control:hover:before, .bg-inverse .btn-warning.disabled.focus.custom-file-control:before, .bg-inverse .btn.btn-warning.disabled.focus, .bg-inverse .btn.btn-warning.disabled:focus, .bg-inverse .btn.btn-warning.disabled:hover, .bg-inverse .btn.btn-warning:disabled.focus, .bg-inverse .btn.btn-warning:disabled:focus, .bg-inverse .btn.btn-warning:disabled:hover { - background-color: transparent; - border-color: #ccc -} - -.btn-warning.btn-link.custom-file-control:before, .btn.btn-warning.btn-link { - background-color: transparent -} - -.btn-danger.custom-file-control:before, .btn.btn-danger { - color: #f44336; - background-color: transparent; - border-color: #ccc -} - -.btn-danger.active.custom-file-control:before, .btn-danger.custom-file-control:active:before, .btn-danger.custom-file-control:focus:before, .btn-danger.custom-file-control:hover:before, .btn-danger.focus.custom-file-control:before, .btn.btn-danger.active, .btn.btn-danger.focus, .btn.btn-danger:active, .btn.btn-danger:focus, .btn.btn-danger:hover, .open > .btn-danger.dropdown-toggle.custom-file-control:before, .open > .btn.btn-danger.dropdown-toggle { - color: #f44336; - background-color: hsla(0, 0%, 60%, .2); - border-color: hsla(0, 0%, 60%, .2) -} - -.btn-danger.active.custom-file-control:focus:before, .btn-danger.active.custom-file-control:hover:before, .btn-danger.active.focus.custom-file-control:before, .btn-danger.custom-file-control:active.focus:before, .btn-danger.custom-file-control:active:focus:before, .btn-danger.custom-file-control:active:hover:before, .btn.btn-danger.active.focus, .btn.btn-danger.active:focus, .btn.btn-danger.active:hover, .btn.btn-danger:active.focus, .btn.btn-danger:active:focus, .btn.btn-danger:active:hover, .open > .btn-danger.dropdown-toggle.custom-file-control:focus:before, .open > .btn-danger.dropdown-toggle.custom-file-control:hover:before, .open > .btn-danger.dropdown-toggle.focus.custom-file-control:before, .open > .btn.btn-danger.dropdown-toggle.focus, .open > .btn.btn-danger.dropdown-toggle:focus, .open > .btn.btn-danger.dropdown-toggle:hover { - color: #f44336; - background-color: hsla(0, 0%, 60%, .4); - border-color: hsla(0, 0%, 60%, .4) -} - -.open > .btn-danger.dropdown-toggle.bmd-btn-icon.custom-file-control:before, .open > .btn.btn-danger.dropdown-toggle.bmd-btn-icon { - color: inherit; - background-color: transparent -} - -.open > .btn-danger.dropdown-toggle.bmd-btn-icon.custom-file-control:hover:before, .open > .btn.btn-danger.dropdown-toggle.bmd-btn-icon:hover { - background-color: hsla(0, 0%, 60%, .2) -} - -.btn-danger.custom-file-control:disabled.focus:before, .btn-danger.custom-file-control:disabled:focus:before, .btn-danger.custom-file-control:disabled:hover:before, .btn-danger.disabled.custom-file-control:focus:before, .btn-danger.disabled.custom-file-control:hover:before, .btn-danger.disabled.focus.custom-file-control:before, .btn.btn-danger.disabled.focus, .btn.btn-danger.disabled:focus, .btn.btn-danger.disabled:hover, .btn.btn-danger:disabled.focus, .btn.btn-danger:disabled:focus, .btn.btn-danger:disabled:hover { - background-color: transparent; - border-color: #ccc -} - -.bg-inverse .btn-danger.custom-file-control:before, .bg-inverse .btn.btn-danger { - color: #f44336; - background-color: transparent; - border-color: #ccc -} - -.bg-inverse .btn-danger.active.custom-file-control:before, .bg-inverse .btn-danger.custom-file-control:active:before, .bg-inverse .btn-danger.custom-file-control:focus:before, .bg-inverse .btn-danger.custom-file-control:hover:before, .bg-inverse .btn-danger.focus.custom-file-control:before, .bg-inverse .btn.btn-danger.active, .bg-inverse .btn.btn-danger.focus, .bg-inverse .btn.btn-danger:active, .bg-inverse .btn.btn-danger:focus, .bg-inverse .btn.btn-danger:hover, .open > .bg-inverse .btn-danger.dropdown-toggle.custom-file-control:before, .open > .bg-inverse .btn.btn-danger.dropdown-toggle { - color: #f44336; - background-color: hsla(0, 0%, 80%, .15); - border-color: hsla(0, 0%, 80%, .15) -} - -.bg-inverse .btn-danger.active.custom-file-control:focus:before, .bg-inverse .btn-danger.active.custom-file-control:hover:before, .bg-inverse .btn-danger.active.focus.custom-file-control:before, .bg-inverse .btn-danger.custom-file-control:active.focus:before, .bg-inverse .btn-danger.custom-file-control:active:focus:before, .bg-inverse .btn-danger.custom-file-control:active:hover:before, .bg-inverse .btn.btn-danger.active.focus, .bg-inverse .btn.btn-danger.active:focus, .bg-inverse .btn.btn-danger.active:hover, .bg-inverse .btn.btn-danger:active.focus, .bg-inverse .btn.btn-danger:active:focus, .bg-inverse .btn.btn-danger:active:hover, .open > .bg-inverse .btn-danger.dropdown-toggle.custom-file-control:focus:before, .open > .bg-inverse .btn-danger.dropdown-toggle.custom-file-control:hover:before, .open > .bg-inverse .btn-danger.dropdown-toggle.focus.custom-file-control:before, .open > .bg-inverse .btn.btn-danger.dropdown-toggle.focus, .open > .bg-inverse .btn.btn-danger.dropdown-toggle:focus, .open > .bg-inverse .btn.btn-danger.dropdown-toggle:hover { - color: #f44336; - background-color: hsla(0, 0%, 80%, .25); - border-color: hsla(0, 0%, 80%, .25) -} - -.open > .bg-inverse .btn-danger.dropdown-toggle.bmd-btn-icon.custom-file-control:before, .open > .bg-inverse .btn.btn-danger.dropdown-toggle.bmd-btn-icon { - color: inherit; - background-color: transparent -} - -.open > .bg-inverse .btn-danger.dropdown-toggle.bmd-btn-icon.custom-file-control:hover:before, .open > .bg-inverse .btn.btn-danger.dropdown-toggle.bmd-btn-icon:hover { - background-color: hsla(0, 0%, 80%, .15) -} - -.bg-inverse .btn-danger.custom-file-control:disabled.focus:before, .bg-inverse .btn-danger.custom-file-control:disabled:focus:before, .bg-inverse .btn-danger.custom-file-control:disabled:hover:before, .bg-inverse .btn-danger.disabled.custom-file-control:focus:before, .bg-inverse .btn-danger.disabled.custom-file-control:hover:before, .bg-inverse .btn-danger.disabled.focus.custom-file-control:before, .bg-inverse .btn.btn-danger.disabled.focus, .bg-inverse .btn.btn-danger.disabled:focus, .bg-inverse .btn.btn-danger.disabled:hover, .bg-inverse .btn.btn-danger:disabled.focus, .bg-inverse .btn.btn-danger:disabled:focus, .bg-inverse .btn.btn-danger:disabled:hover { - background-color: transparent; - border-color: #ccc -} - -.btn-danger.btn-link.custom-file-control:before, .btn.btn-danger.btn-link { - background-color: transparent -} - -.btn-light.custom-file-control:before, .btn.btn-light { - color: #f5f5f5; - background-color: transparent; - border-color: #ccc -} - -.btn-light.active.custom-file-control:before, .btn-light.custom-file-control:active:before, .btn-light.custom-file-control:focus:before, .btn-light.custom-file-control:hover:before, .btn-light.focus.custom-file-control:before, .btn.btn-light.active, .btn.btn-light.focus, .btn.btn-light:active, .btn.btn-light:focus, .btn.btn-light:hover, .open > .btn-light.dropdown-toggle.custom-file-control:before, .open > .btn.btn-light.dropdown-toggle { - color: #f5f5f5; - background-color: hsla(0, 0%, 60%, .2); - border-color: hsla(0, 0%, 60%, .2) -} - -.btn-light.active.custom-file-control:focus:before, .btn-light.active.custom-file-control:hover:before, .btn-light.active.focus.custom-file-control:before, .btn-light.custom-file-control:active.focus:before, .btn-light.custom-file-control:active:focus:before, .btn-light.custom-file-control:active:hover:before, .btn.btn-light.active.focus, .btn.btn-light.active:focus, .btn.btn-light.active:hover, .btn.btn-light:active.focus, .btn.btn-light:active:focus, .btn.btn-light:active:hover, .open > .btn-light.dropdown-toggle.custom-file-control:focus:before, .open > .btn-light.dropdown-toggle.custom-file-control:hover:before, .open > .btn-light.dropdown-toggle.focus.custom-file-control:before, .open > .btn.btn-light.dropdown-toggle.focus, .open > .btn.btn-light.dropdown-toggle:focus, .open > .btn.btn-light.dropdown-toggle:hover { - color: #f5f5f5; - background-color: hsla(0, 0%, 60%, .4); - border-color: hsla(0, 0%, 60%, .4) -} - -.open > .btn-light.dropdown-toggle.bmd-btn-icon.custom-file-control:before, .open > .btn.btn-light.dropdown-toggle.bmd-btn-icon { - color: inherit; - background-color: transparent -} - -.open > .btn-light.dropdown-toggle.bmd-btn-icon.custom-file-control:hover:before, .open > .btn.btn-light.dropdown-toggle.bmd-btn-icon:hover { - background-color: hsla(0, 0%, 60%, .2) -} - -.btn-light.custom-file-control:disabled.focus:before, .btn-light.custom-file-control:disabled:focus:before, .btn-light.custom-file-control:disabled:hover:before, .btn-light.disabled.custom-file-control:focus:before, .btn-light.disabled.custom-file-control:hover:before, .btn-light.disabled.focus.custom-file-control:before, .btn.btn-light.disabled.focus, .btn.btn-light.disabled:focus, .btn.btn-light.disabled:hover, .btn.btn-light:disabled.focus, .btn.btn-light:disabled:focus, .btn.btn-light:disabled:hover { - background-color: transparent; - border-color: #ccc -} - -.bg-inverse .btn-light.custom-file-control:before, .bg-inverse .btn.btn-light { - color: #f5f5f5; - background-color: transparent; - border-color: #ccc -} - -.bg-inverse .btn-light.active.custom-file-control:before, .bg-inverse .btn-light.custom-file-control:active:before, .bg-inverse .btn-light.custom-file-control:focus:before, .bg-inverse .btn-light.custom-file-control:hover:before, .bg-inverse .btn-light.focus.custom-file-control:before, .bg-inverse .btn.btn-light.active, .bg-inverse .btn.btn-light.focus, .bg-inverse .btn.btn-light:active, .bg-inverse .btn.btn-light:focus, .bg-inverse .btn.btn-light:hover, .open > .bg-inverse .btn-light.dropdown-toggle.custom-file-control:before, .open > .bg-inverse .btn.btn-light.dropdown-toggle { - color: #f5f5f5; - background-color: hsla(0, 0%, 80%, .15); - border-color: hsla(0, 0%, 80%, .15) -} - -.bg-inverse .btn-light.active.custom-file-control:focus:before, .bg-inverse .btn-light.active.custom-file-control:hover:before, .bg-inverse .btn-light.active.focus.custom-file-control:before, .bg-inverse .btn-light.custom-file-control:active.focus:before, .bg-inverse .btn-light.custom-file-control:active:focus:before, .bg-inverse .btn-light.custom-file-control:active:hover:before, .bg-inverse .btn.btn-light.active.focus, .bg-inverse .btn.btn-light.active:focus, .bg-inverse .btn.btn-light.active:hover, .bg-inverse .btn.btn-light:active.focus, .bg-inverse .btn.btn-light:active:focus, .bg-inverse .btn.btn-light:active:hover, .open > .bg-inverse .btn-light.dropdown-toggle.custom-file-control:focus:before, .open > .bg-inverse .btn-light.dropdown-toggle.custom-file-control:hover:before, .open > .bg-inverse .btn-light.dropdown-toggle.focus.custom-file-control:before, .open > .bg-inverse .btn.btn-light.dropdown-toggle.focus, .open > .bg-inverse .btn.btn-light.dropdown-toggle:focus, .open > .bg-inverse .btn.btn-light.dropdown-toggle:hover { - color: #f5f5f5; - background-color: hsla(0, 0%, 80%, .25); - border-color: hsla(0, 0%, 80%, .25) -} - -.open > .bg-inverse .btn-light.dropdown-toggle.bmd-btn-icon.custom-file-control:before, .open > .bg-inverse .btn.btn-light.dropdown-toggle.bmd-btn-icon { - color: inherit; - background-color: transparent -} - -.open > .bg-inverse .btn-light.dropdown-toggle.bmd-btn-icon.custom-file-control:hover:before, .open > .bg-inverse .btn.btn-light.dropdown-toggle.bmd-btn-icon:hover { - background-color: hsla(0, 0%, 80%, .15) -} - -.bg-inverse .btn-light.custom-file-control:disabled.focus:before, .bg-inverse .btn-light.custom-file-control:disabled:focus:before, .bg-inverse .btn-light.custom-file-control:disabled:hover:before, .bg-inverse .btn-light.disabled.custom-file-control:focus:before, .bg-inverse .btn-light.disabled.custom-file-control:hover:before, .bg-inverse .btn-light.disabled.focus.custom-file-control:before, .bg-inverse .btn.btn-light.disabled.focus, .bg-inverse .btn.btn-light.disabled:focus, .bg-inverse .btn.btn-light.disabled:hover, .bg-inverse .btn.btn-light:disabled.focus, .bg-inverse .btn.btn-light:disabled:focus, .bg-inverse .btn.btn-light:disabled:hover { - background-color: transparent; - border-color: #ccc -} - -.btn-light.btn-link.custom-file-control:before, .btn.btn-light.btn-link { - background-color: transparent -} - -.btn-dark.custom-file-control:before, .btn.btn-dark { - color: #424242; - background-color: transparent; - border-color: #ccc -} - -.btn-dark.active.custom-file-control:before, .btn-dark.custom-file-control:active:before, .btn-dark.custom-file-control:focus:before, .btn-dark.custom-file-control:hover:before, .btn-dark.focus.custom-file-control:before, .btn.btn-dark.active, .btn.btn-dark.focus, .btn.btn-dark:active, .btn.btn-dark:focus, .btn.btn-dark:hover, .open > .btn-dark.dropdown-toggle.custom-file-control:before, .open > .btn.btn-dark.dropdown-toggle { - color: #424242; - background-color: hsla(0, 0%, 60%, .2); - border-color: hsla(0, 0%, 60%, .2) -} - -.btn-dark.active.custom-file-control:focus:before, .btn-dark.active.custom-file-control:hover:before, .btn-dark.active.focus.custom-file-control:before, .btn-dark.custom-file-control:active.focus:before, .btn-dark.custom-file-control:active:focus:before, .btn-dark.custom-file-control:active:hover:before, .btn.btn-dark.active.focus, .btn.btn-dark.active:focus, .btn.btn-dark.active:hover, .btn.btn-dark:active.focus, .btn.btn-dark:active:focus, .btn.btn-dark:active:hover, .open > .btn-dark.dropdown-toggle.custom-file-control:focus:before, .open > .btn-dark.dropdown-toggle.custom-file-control:hover:before, .open > .btn-dark.dropdown-toggle.focus.custom-file-control:before, .open > .btn.btn-dark.dropdown-toggle.focus, .open > .btn.btn-dark.dropdown-toggle:focus, .open > .btn.btn-dark.dropdown-toggle:hover { - color: #424242; - background-color: hsla(0, 0%, 60%, .4); - border-color: hsla(0, 0%, 60%, .4) -} - -.open > .btn-dark.dropdown-toggle.bmd-btn-icon.custom-file-control:before, .open > .btn.btn-dark.dropdown-toggle.bmd-btn-icon { - color: inherit; - background-color: transparent -} - -.open > .btn-dark.dropdown-toggle.bmd-btn-icon.custom-file-control:hover:before, .open > .btn.btn-dark.dropdown-toggle.bmd-btn-icon:hover { - background-color: hsla(0, 0%, 60%, .2) -} - -.btn-dark.custom-file-control:disabled.focus:before, .btn-dark.custom-file-control:disabled:focus:before, .btn-dark.custom-file-control:disabled:hover:before, .btn-dark.disabled.custom-file-control:focus:before, .btn-dark.disabled.custom-file-control:hover:before, .btn-dark.disabled.focus.custom-file-control:before, .btn.btn-dark.disabled.focus, .btn.btn-dark.disabled:focus, .btn.btn-dark.disabled:hover, .btn.btn-dark:disabled.focus, .btn.btn-dark:disabled:focus, .btn.btn-dark:disabled:hover { - background-color: transparent; - border-color: #ccc -} - -.bg-inverse .btn-dark.custom-file-control:before, .bg-inverse .btn.btn-dark { - color: #424242; - background-color: transparent; - border-color: #ccc -} - -.bg-inverse .btn-dark.active.custom-file-control:before, .bg-inverse .btn-dark.custom-file-control:active:before, .bg-inverse .btn-dark.custom-file-control:focus:before, .bg-inverse .btn-dark.custom-file-control:hover:before, .bg-inverse .btn-dark.focus.custom-file-control:before, .bg-inverse .btn.btn-dark.active, .bg-inverse .btn.btn-dark.focus, .bg-inverse .btn.btn-dark:active, .bg-inverse .btn.btn-dark:focus, .bg-inverse .btn.btn-dark:hover, .open > .bg-inverse .btn-dark.dropdown-toggle.custom-file-control:before, .open > .bg-inverse .btn.btn-dark.dropdown-toggle { - color: #424242; - background-color: hsla(0, 0%, 80%, .15); - border-color: hsla(0, 0%, 80%, .15) -} - -.bg-inverse .btn-dark.active.custom-file-control:focus:before, .bg-inverse .btn-dark.active.custom-file-control:hover:before, .bg-inverse .btn-dark.active.focus.custom-file-control:before, .bg-inverse .btn-dark.custom-file-control:active.focus:before, .bg-inverse .btn-dark.custom-file-control:active:focus:before, .bg-inverse .btn-dark.custom-file-control:active:hover:before, .bg-inverse .btn.btn-dark.active.focus, .bg-inverse .btn.btn-dark.active:focus, .bg-inverse .btn.btn-dark.active:hover, .bg-inverse .btn.btn-dark:active.focus, .bg-inverse .btn.btn-dark:active:focus, .bg-inverse .btn.btn-dark:active:hover, .open > .bg-inverse .btn-dark.dropdown-toggle.custom-file-control:focus:before, .open > .bg-inverse .btn-dark.dropdown-toggle.custom-file-control:hover:before, .open > .bg-inverse .btn-dark.dropdown-toggle.focus.custom-file-control:before, .open > .bg-inverse .btn.btn-dark.dropdown-toggle.focus, .open > .bg-inverse .btn.btn-dark.dropdown-toggle:focus, .open > .bg-inverse .btn.btn-dark.dropdown-toggle:hover { - color: #424242; - background-color: hsla(0, 0%, 80%, .25); - border-color: hsla(0, 0%, 80%, .25) -} - -.open > .bg-inverse .btn-dark.dropdown-toggle.bmd-btn-icon.custom-file-control:before, .open > .bg-inverse .btn.btn-dark.dropdown-toggle.bmd-btn-icon { - color: inherit; - background-color: transparent -} - -.open > .bg-inverse .btn-dark.dropdown-toggle.bmd-btn-icon.custom-file-control:hover:before, .open > .bg-inverse .btn.btn-dark.dropdown-toggle.bmd-btn-icon:hover { - background-color: hsla(0, 0%, 80%, .15) -} - -.bg-inverse .btn-dark.custom-file-control:disabled.focus:before, .bg-inverse .btn-dark.custom-file-control:disabled:focus:before, .bg-inverse .btn-dark.custom-file-control:disabled:hover:before, .bg-inverse .btn-dark.disabled.custom-file-control:focus:before, .bg-inverse .btn-dark.disabled.custom-file-control:hover:before, .bg-inverse .btn-dark.disabled.focus.custom-file-control:before, .bg-inverse .btn.btn-dark.disabled.focus, .bg-inverse .btn.btn-dark.disabled:focus, .bg-inverse .btn.btn-dark.disabled:hover, .bg-inverse .btn.btn-dark:disabled.focus, .bg-inverse .btn.btn-dark:disabled:focus, .bg-inverse .btn.btn-dark:disabled:hover { - background-color: transparent; - border-color: #ccc -} - -.btn-dark.btn-link.custom-file-control:before, .btn.btn-dark.btn-link { - background-color: transparent -} - -.bmd-btn-fab.btn-primary.custom-file-control:before, .btn-group-raised .btn-primary.custom-file-control:before, .btn-group-raised .btn.btn-primary, .btn-raised.btn-primary.custom-file-control:before, .btn.bmd-btn-fab.btn-primary, .btn.btn-raised.btn-primary { - color: #fff; - background-color: #009688; - border-color: #009688 -} - -.bmd-btn-fab.btn-primary.active.custom-file-control:before, .bmd-btn-fab.btn-primary.custom-file-control:active:before, .bmd-btn-fab.btn-primary.custom-file-control:focus:before, .bmd-btn-fab.btn-primary.custom-file-control:hover:before, .bmd-btn-fab.btn-primary.focus.custom-file-control:before, .btn-group-raised .btn-primary.active.custom-file-control:before, .btn-group-raised .btn-primary.custom-file-control:active:before, .btn-group-raised .btn-primary.custom-file-control:focus:before, .btn-group-raised .btn-primary.custom-file-control:hover:before, .btn-group-raised .btn-primary.focus.custom-file-control:before, .btn-group-raised .btn.btn-primary.active, .btn-group-raised .btn.btn-primary.focus, .btn-group-raised .btn.btn-primary:active, .btn-group-raised .btn.btn-primary:focus, .btn-group-raised .btn.btn-primary:hover, .btn-raised.btn-primary.active.custom-file-control:before, .btn-raised.btn-primary.custom-file-control:active:before, .btn-raised.btn-primary.custom-file-control:focus:before, .btn-raised.btn-primary.custom-file-control:hover:before, .btn-raised.btn-primary.focus.custom-file-control:before, .btn.bmd-btn-fab.btn-primary.active, .btn.bmd-btn-fab.btn-primary.focus, .btn.bmd-btn-fab.btn-primary:active, .btn.bmd-btn-fab.btn-primary:focus, .btn.bmd-btn-fab.btn-primary:hover, .btn.btn-raised.btn-primary.active, .btn.btn-raised.btn-primary.focus, .btn.btn-raised.btn-primary:active, .btn.btn-raised.btn-primary:focus, .btn.btn-raised.btn-primary:hover, .open > .bmd-btn-fab.btn-primary.dropdown-toggle.custom-file-control:before, .open > .btn-group-raised .btn-primary.dropdown-toggle.custom-file-control:before, .open > .btn-group-raised .btn.btn-primary.dropdown-toggle, .open > .btn-raised.btn-primary.dropdown-toggle.custom-file-control:before, .open > .btn.bmd-btn-fab.btn-primary.dropdown-toggle, .open > .btn.btn-raised.btn-primary.dropdown-toggle { - color: #fff; - background-color: #008276; - border-color: #005951 -} - -.bmd-btn-fab.btn-primary.active.custom-file-control:focus:before, .bmd-btn-fab.btn-primary.active.custom-file-control:hover:before, .bmd-btn-fab.btn-primary.active.focus.custom-file-control:before, .bmd-btn-fab.btn-primary.custom-file-control:active.focus:before, .bmd-btn-fab.btn-primary.custom-file-control:active:focus:before, .bmd-btn-fab.btn-primary.custom-file-control:active:hover:before, .btn-group-raised .btn-primary.active.custom-file-control:focus:before, .btn-group-raised .btn-primary.active.custom-file-control:hover:before, .btn-group-raised .btn-primary.active.focus.custom-file-control:before, .btn-group-raised .btn-primary.custom-file-control:active.focus:before, .btn-group-raised .btn-primary.custom-file-control:active:focus:before, .btn-group-raised .btn-primary.custom-file-control:active:hover:before, .btn-group-raised .btn.btn-primary.active.focus, .btn-group-raised .btn.btn-primary.active:focus, .btn-group-raised .btn.btn-primary.active:hover, .btn-group-raised .btn.btn-primary:active.focus, .btn-group-raised .btn.btn-primary:active:focus, .btn-group-raised .btn.btn-primary:active:hover, .btn-raised.btn-primary.active.custom-file-control:focus:before, .btn-raised.btn-primary.active.custom-file-control:hover:before, .btn-raised.btn-primary.active.focus.custom-file-control:before, .btn-raised.btn-primary.custom-file-control:active.focus:before, .btn-raised.btn-primary.custom-file-control:active:focus:before, .btn-raised.btn-primary.custom-file-control:active:hover:before, .btn.bmd-btn-fab.btn-primary.active.focus, .btn.bmd-btn-fab.btn-primary.active:focus, .btn.bmd-btn-fab.btn-primary.active:hover, .btn.bmd-btn-fab.btn-primary:active.focus, .btn.bmd-btn-fab.btn-primary:active:focus, .btn.bmd-btn-fab.btn-primary:active:hover, .btn.btn-raised.btn-primary.active.focus, .btn.btn-raised.btn-primary.active:focus, .btn.btn-raised.btn-primary.active:hover, .btn.btn-raised.btn-primary:active.focus, .btn.btn-raised.btn-primary:active:focus, .btn.btn-raised.btn-primary:active:hover, .open > .bmd-btn-fab.btn-primary.dropdown-toggle.custom-file-control:focus:before, .open > .bmd-btn-fab.btn-primary.dropdown-toggle.custom-file-control:hover:before, .open > .bmd-btn-fab.btn-primary.dropdown-toggle.focus.custom-file-control:before, .open > .btn-group-raised .btn-primary.dropdown-toggle.custom-file-control:focus:before, .open > .btn-group-raised .btn-primary.dropdown-toggle.custom-file-control:hover:before, .open > .btn-group-raised .btn-primary.dropdown-toggle.focus.custom-file-control:before, .open > .btn-group-raised .btn.btn-primary.dropdown-toggle.focus, .open > .btn-group-raised .btn.btn-primary.dropdown-toggle:focus, .open > .btn-group-raised .btn.btn-primary.dropdown-toggle:hover, .open > .btn-raised.btn-primary.dropdown-toggle.custom-file-control:focus:before, .open > .btn-raised.btn-primary.dropdown-toggle.custom-file-control:hover:before, .open > .btn-raised.btn-primary.dropdown-toggle.focus.custom-file-control:before, .open > .btn.bmd-btn-fab.btn-primary.dropdown-toggle.focus, .open > .btn.bmd-btn-fab.btn-primary.dropdown-toggle:focus, .open > .btn.bmd-btn-fab.btn-primary.dropdown-toggle:hover, .open > .btn.btn-raised.btn-primary.dropdown-toggle.focus, .open > .btn.btn-raised.btn-primary.dropdown-toggle:focus, .open > .btn.btn-raised.btn-primary.dropdown-toggle:hover { - color: #fff; - background-color: #008276; - border-color: #001714 -} - -.open > .bmd-btn-fab.btn-primary.dropdown-toggle.bmd-btn-icon.custom-file-control:before, .open > .btn-group-raised .btn-primary.dropdown-toggle.bmd-btn-icon.custom-file-control:before, .open > .btn-group-raised .btn.btn-primary.dropdown-toggle.bmd-btn-icon, .open > .btn-raised.btn-primary.dropdown-toggle.bmd-btn-icon.custom-file-control:before, .open > .btn.bmd-btn-fab.btn-primary.dropdown-toggle.bmd-btn-icon, .open > .btn.btn-raised.btn-primary.dropdown-toggle.bmd-btn-icon { - color: inherit; - background-color: #009688 -} - -.open > .bmd-btn-fab.btn-primary.dropdown-toggle.bmd-btn-icon.custom-file-control:hover:before, .open > .btn-group-raised .btn-primary.dropdown-toggle.bmd-btn-icon.custom-file-control:hover:before, .open > .btn-group-raised .btn.btn-primary.dropdown-toggle.bmd-btn-icon:hover, .open > .btn-raised.btn-primary.dropdown-toggle.bmd-btn-icon.custom-file-control:hover:before, .open > .btn.bmd-btn-fab.btn-primary.dropdown-toggle.bmd-btn-icon:hover, .open > .btn.btn-raised.btn-primary.dropdown-toggle.bmd-btn-icon:hover { - background-color: #008276 -} - -.bmd-btn-fab.btn-primary.custom-file-control:disabled.focus:before, .bmd-btn-fab.btn-primary.custom-file-control:disabled:focus:before, .bmd-btn-fab.btn-primary.custom-file-control:disabled:hover:before, .bmd-btn-fab.btn-primary.disabled.custom-file-control:focus:before, .bmd-btn-fab.btn-primary.disabled.custom-file-control:hover:before, .bmd-btn-fab.btn-primary.disabled.focus.custom-file-control:before, .btn-group-raised .btn-primary.custom-file-control:disabled.focus:before, .btn-group-raised .btn-primary.custom-file-control:disabled:focus:before, .btn-group-raised .btn-primary.custom-file-control:disabled:hover:before, .btn-group-raised .btn-primary.disabled.custom-file-control:focus:before, .btn-group-raised .btn-primary.disabled.custom-file-control:hover:before, .btn-group-raised .btn-primary.disabled.focus.custom-file-control:before, .btn-group-raised .btn.btn-primary.disabled.focus, .btn-group-raised .btn.btn-primary.disabled:focus, .btn-group-raised .btn.btn-primary.disabled:hover, .btn-group-raised .btn.btn-primary:disabled.focus, .btn-group-raised .btn.btn-primary:disabled:focus, .btn-group-raised .btn.btn-primary:disabled:hover, .btn-raised.btn-primary.custom-file-control:disabled.focus:before, .btn-raised.btn-primary.custom-file-control:disabled:focus:before, .btn-raised.btn-primary.custom-file-control:disabled:hover:before, .btn-raised.btn-primary.disabled.custom-file-control:focus:before, .btn-raised.btn-primary.disabled.custom-file-control:hover:before, .btn-raised.btn-primary.disabled.focus.custom-file-control:before, .btn.bmd-btn-fab.btn-primary.disabled.focus, .btn.bmd-btn-fab.btn-primary.disabled:focus, .btn.bmd-btn-fab.btn-primary.disabled:hover, .btn.bmd-btn-fab.btn-primary:disabled.focus, .btn.bmd-btn-fab.btn-primary:disabled:focus, .btn.bmd-btn-fab.btn-primary:disabled:hover, .btn.btn-raised.btn-primary.disabled.focus, .btn.btn-raised.btn-primary.disabled:focus, .btn.btn-raised.btn-primary.disabled:hover, .btn.btn-raised.btn-primary:disabled.focus, .btn.btn-raised.btn-primary:disabled:focus, .btn.btn-raised.btn-primary:disabled:hover { - background-color: #009688; - border-color: #009688 -} - -.bmd-btn-fab.btn-secondary.custom-file-control:before, .btn-group-raised .btn-secondary.custom-file-control:before, .btn-group-raised .btn.btn-secondary, .btn-raised.btn-secondary.custom-file-control:before, .btn.bmd-btn-fab.btn-secondary, .btn.btn-raised.btn-secondary { - color: #fff; - background-color: #6c757d; - border-color: #6c757d -} - -.bmd-btn-fab.btn-secondary.active.custom-file-control:before, .bmd-btn-fab.btn-secondary.custom-file-control:active:before, .bmd-btn-fab.btn-secondary.custom-file-control:focus:before, .bmd-btn-fab.btn-secondary.custom-file-control:hover:before, .bmd-btn-fab.btn-secondary.focus.custom-file-control:before, .btn-group-raised .btn-secondary.active.custom-file-control:before, .btn-group-raised .btn-secondary.custom-file-control:active:before, .btn-group-raised .btn-secondary.custom-file-control:focus:before, .btn-group-raised .btn-secondary.custom-file-control:hover:before, .btn-group-raised .btn-secondary.focus.custom-file-control:before, .btn-group-raised .btn.btn-secondary.active, .btn-group-raised .btn.btn-secondary.focus, .btn-group-raised .btn.btn-secondary:active, .btn-group-raised .btn.btn-secondary:focus, .btn-group-raised .btn.btn-secondary:hover, .btn-raised.btn-secondary.active.custom-file-control:before, .btn-raised.btn-secondary.custom-file-control:active:before, .btn-raised.btn-secondary.custom-file-control:focus:before, .btn-raised.btn-secondary.custom-file-control:hover:before, .btn-raised.btn-secondary.focus.custom-file-control:before, .btn.bmd-btn-fab.btn-secondary.active, .btn.bmd-btn-fab.btn-secondary.focus, .btn.bmd-btn-fab.btn-secondary:active, .btn.bmd-btn-fab.btn-secondary:focus, .btn.bmd-btn-fab.btn-secondary:hover, .btn.btn-raised.btn-secondary.active, .btn.btn-raised.btn-secondary.focus, .btn.btn-raised.btn-secondary:active, .btn.btn-raised.btn-secondary:focus, .btn.btn-raised.btn-secondary:hover, .open > .bmd-btn-fab.btn-secondary.dropdown-toggle.custom-file-control:before, .open > .btn-group-raised .btn-secondary.dropdown-toggle.custom-file-control:before, .open > .btn-group-raised .btn.btn-secondary.dropdown-toggle, .open > .btn-raised.btn-secondary.dropdown-toggle.custom-file-control:before, .open > .btn.bmd-btn-fab.btn-secondary.dropdown-toggle, .open > .btn.btn-raised.btn-secondary.dropdown-toggle { - color: #fff; - background-color: #636b72; - border-color: #50565c -} - -.bmd-btn-fab.btn-secondary.active.custom-file-control:focus:before, .bmd-btn-fab.btn-secondary.active.custom-file-control:hover:before, .bmd-btn-fab.btn-secondary.active.focus.custom-file-control:before, .bmd-btn-fab.btn-secondary.custom-file-control:active.focus:before, .bmd-btn-fab.btn-secondary.custom-file-control:active:focus:before, .bmd-btn-fab.btn-secondary.custom-file-control:active:hover:before, .btn-group-raised .btn-secondary.active.custom-file-control:focus:before, .btn-group-raised .btn-secondary.active.custom-file-control:hover:before, .btn-group-raised .btn-secondary.active.focus.custom-file-control:before, .btn-group-raised .btn-secondary.custom-file-control:active.focus:before, .btn-group-raised .btn-secondary.custom-file-control:active:focus:before, .btn-group-raised .btn-secondary.custom-file-control:active:hover:before, .btn-group-raised .btn.btn-secondary.active.focus, .btn-group-raised .btn.btn-secondary.active:focus, .btn-group-raised .btn.btn-secondary.active:hover, .btn-group-raised .btn.btn-secondary:active.focus, .btn-group-raised .btn.btn-secondary:active:focus, .btn-group-raised .btn.btn-secondary:active:hover, .btn-raised.btn-secondary.active.custom-file-control:focus:before, .btn-raised.btn-secondary.active.custom-file-control:hover:before, .btn-raised.btn-secondary.active.focus.custom-file-control:before, .btn-raised.btn-secondary.custom-file-control:active.focus:before, .btn-raised.btn-secondary.custom-file-control:active:focus:before, .btn-raised.btn-secondary.custom-file-control:active:hover:before, .btn.bmd-btn-fab.btn-secondary.active.focus, .btn.bmd-btn-fab.btn-secondary.active:focus, .btn.bmd-btn-fab.btn-secondary.active:hover, .btn.bmd-btn-fab.btn-secondary:active.focus, .btn.bmd-btn-fab.btn-secondary:active:focus, .btn.bmd-btn-fab.btn-secondary:active:hover, .btn.btn-raised.btn-secondary.active.focus, .btn.btn-raised.btn-secondary.active:focus, .btn.btn-raised.btn-secondary.active:hover, .btn.btn-raised.btn-secondary:active.focus, .btn.btn-raised.btn-secondary:active:focus, .btn.btn-raised.btn-secondary:active:hover, .open > .bmd-btn-fab.btn-secondary.dropdown-toggle.custom-file-control:focus:before, .open > .bmd-btn-fab.btn-secondary.dropdown-toggle.custom-file-control:hover:before, .open > .bmd-btn-fab.btn-secondary.dropdown-toggle.focus.custom-file-control:before, .open > .btn-group-raised .btn-secondary.dropdown-toggle.custom-file-control:focus:before, .open > .btn-group-raised .btn-secondary.dropdown-toggle.custom-file-control:hover:before, .open > .btn-group-raised .btn-secondary.dropdown-toggle.focus.custom-file-control:before, .open > .btn-group-raised .btn.btn-secondary.dropdown-toggle.focus, .open > .btn-group-raised .btn.btn-secondary.dropdown-toggle:focus, .open > .btn-group-raised .btn.btn-secondary.dropdown-toggle:hover, .open > .btn-raised.btn-secondary.dropdown-toggle.custom-file-control:focus:before, .open > .btn-raised.btn-secondary.dropdown-toggle.custom-file-control:hover:before, .open > .btn-raised.btn-secondary.dropdown-toggle.focus.custom-file-control:before, .open > .btn.bmd-btn-fab.btn-secondary.dropdown-toggle.focus, .open > .btn.bmd-btn-fab.btn-secondary.dropdown-toggle:focus, .open > .btn.bmd-btn-fab.btn-secondary.dropdown-toggle:hover, .open > .btn.btn-raised.btn-secondary.dropdown-toggle.focus, .open > .btn.btn-raised.btn-secondary.dropdown-toggle:focus, .open > .btn.btn-raised.btn-secondary.dropdown-toggle:hover { - color: #fff; - background-color: #636b72; - border-color: #313539 -} - -.open > .bmd-btn-fab.btn-secondary.dropdown-toggle.bmd-btn-icon.custom-file-control:before, .open > .btn-group-raised .btn-secondary.dropdown-toggle.bmd-btn-icon.custom-file-control:before, .open > .btn-group-raised .btn.btn-secondary.dropdown-toggle.bmd-btn-icon, .open > .btn-raised.btn-secondary.dropdown-toggle.bmd-btn-icon.custom-file-control:before, .open > .btn.bmd-btn-fab.btn-secondary.dropdown-toggle.bmd-btn-icon, .open > .btn.btn-raised.btn-secondary.dropdown-toggle.bmd-btn-icon { - color: inherit; - background-color: #6c757d -} - -.open > .bmd-btn-fab.btn-secondary.dropdown-toggle.bmd-btn-icon.custom-file-control:hover:before, .open > .btn-group-raised .btn-secondary.dropdown-toggle.bmd-btn-icon.custom-file-control:hover:before, .open > .btn-group-raised .btn.btn-secondary.dropdown-toggle.bmd-btn-icon:hover, .open > .btn-raised.btn-secondary.dropdown-toggle.bmd-btn-icon.custom-file-control:hover:before, .open > .btn.bmd-btn-fab.btn-secondary.dropdown-toggle.bmd-btn-icon:hover, .open > .btn.btn-raised.btn-secondary.dropdown-toggle.bmd-btn-icon:hover { - background-color: #636b72 -} - -.bmd-btn-fab.btn-secondary.custom-file-control:disabled.focus:before, .bmd-btn-fab.btn-secondary.custom-file-control:disabled:focus:before, .bmd-btn-fab.btn-secondary.custom-file-control:disabled:hover:before, .bmd-btn-fab.btn-secondary.disabled.custom-file-control:focus:before, .bmd-btn-fab.btn-secondary.disabled.custom-file-control:hover:before, .bmd-btn-fab.btn-secondary.disabled.focus.custom-file-control:before, .btn-group-raised .btn-secondary.custom-file-control:disabled.focus:before, .btn-group-raised .btn-secondary.custom-file-control:disabled:focus:before, .btn-group-raised .btn-secondary.custom-file-control:disabled:hover:before, .btn-group-raised .btn-secondary.disabled.custom-file-control:focus:before, .btn-group-raised .btn-secondary.disabled.custom-file-control:hover:before, .btn-group-raised .btn-secondary.disabled.focus.custom-file-control:before, .btn-group-raised .btn.btn-secondary.disabled.focus, .btn-group-raised .btn.btn-secondary.disabled:focus, .btn-group-raised .btn.btn-secondary.disabled:hover, .btn-group-raised .btn.btn-secondary:disabled.focus, .btn-group-raised .btn.btn-secondary:disabled:focus, .btn-group-raised .btn.btn-secondary:disabled:hover, .btn-raised.btn-secondary.custom-file-control:disabled.focus:before, .btn-raised.btn-secondary.custom-file-control:disabled:focus:before, .btn-raised.btn-secondary.custom-file-control:disabled:hover:before, .btn-raised.btn-secondary.disabled.custom-file-control:focus:before, .btn-raised.btn-secondary.disabled.custom-file-control:hover:before, .btn-raised.btn-secondary.disabled.focus.custom-file-control:before, .btn.bmd-btn-fab.btn-secondary.disabled.focus, .btn.bmd-btn-fab.btn-secondary.disabled:focus, .btn.bmd-btn-fab.btn-secondary.disabled:hover, .btn.bmd-btn-fab.btn-secondary:disabled.focus, .btn.bmd-btn-fab.btn-secondary:disabled:focus, .btn.bmd-btn-fab.btn-secondary:disabled:hover, .btn.btn-raised.btn-secondary.disabled.focus, .btn.btn-raised.btn-secondary.disabled:focus, .btn.btn-raised.btn-secondary.disabled:hover, .btn.btn-raised.btn-secondary:disabled.focus, .btn.btn-raised.btn-secondary:disabled:focus, .btn.btn-raised.btn-secondary:disabled:hover { - background-color: #6c757d; - border-color: #6c757d -} - -.bmd-btn-fab.btn-success.custom-file-control:before, .btn-group-raised .btn-success.custom-file-control:before, .btn-group-raised .btn.btn-success, .btn-raised.btn-success.custom-file-control:before, .btn.bmd-btn-fab.btn-success, .btn.btn-raised.btn-success { - color: #fff; - background-color: #4caf50; - border-color: #4caf50 -} - -.bmd-btn-fab.btn-success.active.custom-file-control:before, .bmd-btn-fab.btn-success.custom-file-control:active:before, .bmd-btn-fab.btn-success.custom-file-control:focus:before, .bmd-btn-fab.btn-success.custom-file-control:hover:before, .bmd-btn-fab.btn-success.focus.custom-file-control:before, .btn-group-raised .btn-success.active.custom-file-control:before, .btn-group-raised .btn-success.custom-file-control:active:before, .btn-group-raised .btn-success.custom-file-control:focus:before, .btn-group-raised .btn-success.custom-file-control:hover:before, .btn-group-raised .btn-success.focus.custom-file-control:before, .btn-group-raised .btn.btn-success.active, .btn-group-raised .btn.btn-success.focus, .btn-group-raised .btn.btn-success:active, .btn-group-raised .btn.btn-success:focus, .btn-group-raised .btn.btn-success:hover, .btn-raised.btn-success.active.custom-file-control:before, .btn-raised.btn-success.custom-file-control:active:before, .btn-raised.btn-success.custom-file-control:focus:before, .btn-raised.btn-success.custom-file-control:hover:before, .btn-raised.btn-success.focus.custom-file-control:before, .btn.bmd-btn-fab.btn-success.active, .btn.bmd-btn-fab.btn-success.focus, .btn.bmd-btn-fab.btn-success:active, .btn.bmd-btn-fab.btn-success:focus, .btn.bmd-btn-fab.btn-success:hover, .btn.btn-raised.btn-success.active, .btn.btn-raised.btn-success.focus, .btn.btn-raised.btn-success:active, .btn.btn-raised.btn-success:focus, .btn.btn-raised.btn-success:hover, .open > .bmd-btn-fab.btn-success.dropdown-toggle.custom-file-control:before, .open > .btn-group-raised .btn-success.dropdown-toggle.custom-file-control:before, .open > .btn-group-raised .btn.btn-success.dropdown-toggle, .open > .btn-raised.btn-success.dropdown-toggle.custom-file-control:before, .open > .btn.bmd-btn-fab.btn-success.dropdown-toggle, .open > .btn.btn-raised.btn-success.dropdown-toggle { - color: #fff; - background-color: #46a149; - border-color: #39843c -} - -.bmd-btn-fab.btn-success.active.custom-file-control:focus:before, .bmd-btn-fab.btn-success.active.custom-file-control:hover:before, .bmd-btn-fab.btn-success.active.focus.custom-file-control:before, .bmd-btn-fab.btn-success.custom-file-control:active.focus:before, .bmd-btn-fab.btn-success.custom-file-control:active:focus:before, .bmd-btn-fab.btn-success.custom-file-control:active:hover:before, .btn-group-raised .btn-success.active.custom-file-control:focus:before, .btn-group-raised .btn-success.active.custom-file-control:hover:before, .btn-group-raised .btn-success.active.focus.custom-file-control:before, .btn-group-raised .btn-success.custom-file-control:active.focus:before, .btn-group-raised .btn-success.custom-file-control:active:focus:before, .btn-group-raised .btn-success.custom-file-control:active:hover:before, .btn-group-raised .btn.btn-success.active.focus, .btn-group-raised .btn.btn-success.active:focus, .btn-group-raised .btn.btn-success.active:hover, .btn-group-raised .btn.btn-success:active.focus, .btn-group-raised .btn.btn-success:active:focus, .btn-group-raised .btn.btn-success:active:hover, .btn-raised.btn-success.active.custom-file-control:focus:before, .btn-raised.btn-success.active.custom-file-control:hover:before, .btn-raised.btn-success.active.focus.custom-file-control:before, .btn-raised.btn-success.custom-file-control:active.focus:before, .btn-raised.btn-success.custom-file-control:active:focus:before, .btn-raised.btn-success.custom-file-control:active:hover:before, .btn.bmd-btn-fab.btn-success.active.focus, .btn.bmd-btn-fab.btn-success.active:focus, .btn.bmd-btn-fab.btn-success.active:hover, .btn.bmd-btn-fab.btn-success:active.focus, .btn.bmd-btn-fab.btn-success:active:focus, .btn.bmd-btn-fab.btn-success:active:hover, .btn.btn-raised.btn-success.active.focus, .btn.btn-raised.btn-success.active:focus, .btn.btn-raised.btn-success.active:hover, .btn.btn-raised.btn-success:active.focus, .btn.btn-raised.btn-success:active:focus, .btn.btn-raised.btn-success:active:hover, .open > .bmd-btn-fab.btn-success.dropdown-toggle.custom-file-control:focus:before, .open > .bmd-btn-fab.btn-success.dropdown-toggle.custom-file-control:hover:before, .open > .bmd-btn-fab.btn-success.dropdown-toggle.focus.custom-file-control:before, .open > .btn-group-raised .btn-success.dropdown-toggle.custom-file-control:focus:before, .open > .btn-group-raised .btn-success.dropdown-toggle.custom-file-control:hover:before, .open > .btn-group-raised .btn-success.dropdown-toggle.focus.custom-file-control:before, .open > .btn-group-raised .btn.btn-success.dropdown-toggle.focus, .open > .btn-group-raised .btn.btn-success.dropdown-toggle:focus, .open > .btn-group-raised .btn.btn-success.dropdown-toggle:hover, .open > .btn-raised.btn-success.dropdown-toggle.custom-file-control:focus:before, .open > .btn-raised.btn-success.dropdown-toggle.custom-file-control:hover:before, .open > .btn-raised.btn-success.dropdown-toggle.focus.custom-file-control:before, .open > .btn.bmd-btn-fab.btn-success.dropdown-toggle.focus, .open > .btn.bmd-btn-fab.btn-success.dropdown-toggle:focus, .open > .btn.bmd-btn-fab.btn-success.dropdown-toggle:hover, .open > .btn.btn-raised.btn-success.dropdown-toggle.focus, .open > .btn.btn-raised.btn-success.dropdown-toggle:focus, .open > .btn.btn-raised.btn-success.dropdown-toggle:hover { - color: #fff; - background-color: #46a149; - border-color: #255627 -} - -.open > .bmd-btn-fab.btn-success.dropdown-toggle.bmd-btn-icon.custom-file-control:before, .open > .btn-group-raised .btn-success.dropdown-toggle.bmd-btn-icon.custom-file-control:before, .open > .btn-group-raised .btn.btn-success.dropdown-toggle.bmd-btn-icon, .open > .btn-raised.btn-success.dropdown-toggle.bmd-btn-icon.custom-file-control:before, .open > .btn.bmd-btn-fab.btn-success.dropdown-toggle.bmd-btn-icon, .open > .btn.btn-raised.btn-success.dropdown-toggle.bmd-btn-icon { - color: inherit; - background-color: #4caf50 -} - -.open > .bmd-btn-fab.btn-success.dropdown-toggle.bmd-btn-icon.custom-file-control:hover:before, .open > .btn-group-raised .btn-success.dropdown-toggle.bmd-btn-icon.custom-file-control:hover:before, .open > .btn-group-raised .btn.btn-success.dropdown-toggle.bmd-btn-icon:hover, .open > .btn-raised.btn-success.dropdown-toggle.bmd-btn-icon.custom-file-control:hover:before, .open > .btn.bmd-btn-fab.btn-success.dropdown-toggle.bmd-btn-icon:hover, .open > .btn.btn-raised.btn-success.dropdown-toggle.bmd-btn-icon:hover { - background-color: #46a149 -} - -.bmd-btn-fab.btn-success.custom-file-control:disabled.focus:before, .bmd-btn-fab.btn-success.custom-file-control:disabled:focus:before, .bmd-btn-fab.btn-success.custom-file-control:disabled:hover:before, .bmd-btn-fab.btn-success.disabled.custom-file-control:focus:before, .bmd-btn-fab.btn-success.disabled.custom-file-control:hover:before, .bmd-btn-fab.btn-success.disabled.focus.custom-file-control:before, .btn-group-raised .btn-success.custom-file-control:disabled.focus:before, .btn-group-raised .btn-success.custom-file-control:disabled:focus:before, .btn-group-raised .btn-success.custom-file-control:disabled:hover:before, .btn-group-raised .btn-success.disabled.custom-file-control:focus:before, .btn-group-raised .btn-success.disabled.custom-file-control:hover:before, .btn-group-raised .btn-success.disabled.focus.custom-file-control:before, .btn-group-raised .btn.btn-success.disabled.focus, .btn-group-raised .btn.btn-success.disabled:focus, .btn-group-raised .btn.btn-success.disabled:hover, .btn-group-raised .btn.btn-success:disabled.focus, .btn-group-raised .btn.btn-success:disabled:focus, .btn-group-raised .btn.btn-success:disabled:hover, .btn-raised.btn-success.custom-file-control:disabled.focus:before, .btn-raised.btn-success.custom-file-control:disabled:focus:before, .btn-raised.btn-success.custom-file-control:disabled:hover:before, .btn-raised.btn-success.disabled.custom-file-control:focus:before, .btn-raised.btn-success.disabled.custom-file-control:hover:before, .btn-raised.btn-success.disabled.focus.custom-file-control:before, .btn.bmd-btn-fab.btn-success.disabled.focus, .btn.bmd-btn-fab.btn-success.disabled:focus, .btn.bmd-btn-fab.btn-success.disabled:hover, .btn.bmd-btn-fab.btn-success:disabled.focus, .btn.bmd-btn-fab.btn-success:disabled:focus, .btn.bmd-btn-fab.btn-success:disabled:hover, .btn.btn-raised.btn-success.disabled.focus, .btn.btn-raised.btn-success.disabled:focus, .btn.btn-raised.btn-success.disabled:hover, .btn.btn-raised.btn-success:disabled.focus, .btn.btn-raised.btn-success:disabled:focus, .btn.btn-raised.btn-success:disabled:hover { - background-color: #4caf50; - border-color: #4caf50 -} - -.bmd-btn-fab.btn-info.custom-file-control:before, .btn-group-raised .btn-info.custom-file-control:before, .btn-group-raised .btn.btn-info, .btn-raised.btn-info.custom-file-control:before, .btn.bmd-btn-fab.btn-info, .btn.btn-raised.btn-info { - color: #fff; - background-color: #03a9f4; - border-color: #03a9f4 -} - -.bmd-btn-fab.btn-info.active.custom-file-control:before, .bmd-btn-fab.btn-info.custom-file-control:active:before, .bmd-btn-fab.btn-info.custom-file-control:focus:before, .bmd-btn-fab.btn-info.custom-file-control:hover:before, .bmd-btn-fab.btn-info.focus.custom-file-control:before, .btn-group-raised .btn-info.active.custom-file-control:before, .btn-group-raised .btn-info.custom-file-control:active:before, .btn-group-raised .btn-info.custom-file-control:focus:before, .btn-group-raised .btn-info.custom-file-control:hover:before, .btn-group-raised .btn-info.focus.custom-file-control:before, .btn-group-raised .btn.btn-info.active, .btn-group-raised .btn.btn-info.focus, .btn-group-raised .btn.btn-info:active, .btn-group-raised .btn.btn-info:focus, .btn-group-raised .btn.btn-info:hover, .btn-raised.btn-info.active.custom-file-control:before, .btn-raised.btn-info.custom-file-control:active:before, .btn-raised.btn-info.custom-file-control:focus:before, .btn-raised.btn-info.custom-file-control:hover:before, .btn-raised.btn-info.focus.custom-file-control:before, .btn.bmd-btn-fab.btn-info.active, .btn.bmd-btn-fab.btn-info.focus, .btn.bmd-btn-fab.btn-info:active, .btn.bmd-btn-fab.btn-info:focus, .btn.bmd-btn-fab.btn-info:hover, .btn.btn-raised.btn-info.active, .btn.btn-raised.btn-info.focus, .btn.btn-raised.btn-info:active, .btn.btn-raised.btn-info:focus, .btn.btn-raised.btn-info:hover, .open > .bmd-btn-fab.btn-info.dropdown-toggle.custom-file-control:before, .open > .btn-group-raised .btn-info.dropdown-toggle.custom-file-control:before, .open > .btn-group-raised .btn.btn-info.dropdown-toggle, .open > .btn-raised.btn-info.dropdown-toggle.custom-file-control:before, .open > .btn.bmd-btn-fab.btn-info.dropdown-toggle, .open > .btn.btn-raised.btn-info.dropdown-toggle { - color: #fff; - background-color: #039be0; - border-color: #027fb8 -} - -.bmd-btn-fab.btn-info.active.custom-file-control:focus:before, .bmd-btn-fab.btn-info.active.custom-file-control:hover:before, .bmd-btn-fab.btn-info.active.focus.custom-file-control:before, .bmd-btn-fab.btn-info.custom-file-control:active.focus:before, .bmd-btn-fab.btn-info.custom-file-control:active:focus:before, .bmd-btn-fab.btn-info.custom-file-control:active:hover:before, .btn-group-raised .btn-info.active.custom-file-control:focus:before, .btn-group-raised .btn-info.active.custom-file-control:hover:before, .btn-group-raised .btn-info.active.focus.custom-file-control:before, .btn-group-raised .btn-info.custom-file-control:active.focus:before, .btn-group-raised .btn-info.custom-file-control:active:focus:before, .btn-group-raised .btn-info.custom-file-control:active:hover:before, .btn-group-raised .btn.btn-info.active.focus, .btn-group-raised .btn.btn-info.active:focus, .btn-group-raised .btn.btn-info.active:hover, .btn-group-raised .btn.btn-info:active.focus, .btn-group-raised .btn.btn-info:active:focus, .btn-group-raised .btn.btn-info:active:hover, .btn-raised.btn-info.active.custom-file-control:focus:before, .btn-raised.btn-info.active.custom-file-control:hover:before, .btn-raised.btn-info.active.focus.custom-file-control:before, .btn-raised.btn-info.custom-file-control:active.focus:before, .btn-raised.btn-info.custom-file-control:active:focus:before, .btn-raised.btn-info.custom-file-control:active:hover:before, .btn.bmd-btn-fab.btn-info.active.focus, .btn.bmd-btn-fab.btn-info.active:focus, .btn.bmd-btn-fab.btn-info.active:hover, .btn.bmd-btn-fab.btn-info:active.focus, .btn.bmd-btn-fab.btn-info:active:focus, .btn.bmd-btn-fab.btn-info:active:hover, .btn.btn-raised.btn-info.active.focus, .btn.btn-raised.btn-info.active:focus, .btn.btn-raised.btn-info.active:hover, .btn.btn-raised.btn-info:active.focus, .btn.btn-raised.btn-info:active:focus, .btn.btn-raised.btn-info:active:hover, .open > .bmd-btn-fab.btn-info.dropdown-toggle.custom-file-control:focus:before, .open > .bmd-btn-fab.btn-info.dropdown-toggle.custom-file-control:hover:before, .open > .bmd-btn-fab.btn-info.dropdown-toggle.focus.custom-file-control:before, .open > .btn-group-raised .btn-info.dropdown-toggle.custom-file-control:focus:before, .open > .btn-group-raised .btn-info.dropdown-toggle.custom-file-control:hover:before, .open > .btn-group-raised .btn-info.dropdown-toggle.focus.custom-file-control:before, .open > .btn-group-raised .btn.btn-info.dropdown-toggle.focus, .open > .btn-group-raised .btn.btn-info.dropdown-toggle:focus, .open > .btn-group-raised .btn.btn-info.dropdown-toggle:hover, .open > .btn-raised.btn-info.dropdown-toggle.custom-file-control:focus:before, .open > .btn-raised.btn-info.dropdown-toggle.custom-file-control:hover:before, .open > .btn-raised.btn-info.dropdown-toggle.focus.custom-file-control:before, .open > .btn.bmd-btn-fab.btn-info.dropdown-toggle.focus, .open > .btn.bmd-btn-fab.btn-info.dropdown-toggle:focus, .open > .btn.bmd-btn-fab.btn-info.dropdown-toggle:hover, .open > .btn.btn-raised.btn-info.dropdown-toggle.focus, .open > .btn.btn-raised.btn-info.dropdown-toggle:focus, .open > .btn.btn-raised.btn-info.dropdown-toggle:hover { - color: #fff; - background-color: #039be0; - border-color: #015276 -} - -.open > .bmd-btn-fab.btn-info.dropdown-toggle.bmd-btn-icon.custom-file-control:before, .open > .btn-group-raised .btn-info.dropdown-toggle.bmd-btn-icon.custom-file-control:before, .open > .btn-group-raised .btn.btn-info.dropdown-toggle.bmd-btn-icon, .open > .btn-raised.btn-info.dropdown-toggle.bmd-btn-icon.custom-file-control:before, .open > .btn.bmd-btn-fab.btn-info.dropdown-toggle.bmd-btn-icon, .open > .btn.btn-raised.btn-info.dropdown-toggle.bmd-btn-icon { - color: inherit; - background-color: #03a9f4 -} - -.open > .bmd-btn-fab.btn-info.dropdown-toggle.bmd-btn-icon.custom-file-control:hover:before, .open > .btn-group-raised .btn-info.dropdown-toggle.bmd-btn-icon.custom-file-control:hover:before, .open > .btn-group-raised .btn.btn-info.dropdown-toggle.bmd-btn-icon:hover, .open > .btn-raised.btn-info.dropdown-toggle.bmd-btn-icon.custom-file-control:hover:before, .open > .btn.bmd-btn-fab.btn-info.dropdown-toggle.bmd-btn-icon:hover, .open > .btn.btn-raised.btn-info.dropdown-toggle.bmd-btn-icon:hover { - background-color: #039be0 -} - -.bmd-btn-fab.btn-info.custom-file-control:disabled.focus:before, .bmd-btn-fab.btn-info.custom-file-control:disabled:focus:before, .bmd-btn-fab.btn-info.custom-file-control:disabled:hover:before, .bmd-btn-fab.btn-info.disabled.custom-file-control:focus:before, .bmd-btn-fab.btn-info.disabled.custom-file-control:hover:before, .bmd-btn-fab.btn-info.disabled.focus.custom-file-control:before, .btn-group-raised .btn-info.custom-file-control:disabled.focus:before, .btn-group-raised .btn-info.custom-file-control:disabled:focus:before, .btn-group-raised .btn-info.custom-file-control:disabled:hover:before, .btn-group-raised .btn-info.disabled.custom-file-control:focus:before, .btn-group-raised .btn-info.disabled.custom-file-control:hover:before, .btn-group-raised .btn-info.disabled.focus.custom-file-control:before, .btn-group-raised .btn.btn-info.disabled.focus, .btn-group-raised .btn.btn-info.disabled:focus, .btn-group-raised .btn.btn-info.disabled:hover, .btn-group-raised .btn.btn-info:disabled.focus, .btn-group-raised .btn.btn-info:disabled:focus, .btn-group-raised .btn.btn-info:disabled:hover, .btn-raised.btn-info.custom-file-control:disabled.focus:before, .btn-raised.btn-info.custom-file-control:disabled:focus:before, .btn-raised.btn-info.custom-file-control:disabled:hover:before, .btn-raised.btn-info.disabled.custom-file-control:focus:before, .btn-raised.btn-info.disabled.custom-file-control:hover:before, .btn-raised.btn-info.disabled.focus.custom-file-control:before, .btn.bmd-btn-fab.btn-info.disabled.focus, .btn.bmd-btn-fab.btn-info.disabled:focus, .btn.bmd-btn-fab.btn-info.disabled:hover, .btn.bmd-btn-fab.btn-info:disabled.focus, .btn.bmd-btn-fab.btn-info:disabled:focus, .btn.bmd-btn-fab.btn-info:disabled:hover, .btn.btn-raised.btn-info.disabled.focus, .btn.btn-raised.btn-info.disabled:focus, .btn.btn-raised.btn-info.disabled:hover, .btn.btn-raised.btn-info:disabled.focus, .btn.btn-raised.btn-info:disabled:focus, .btn.btn-raised.btn-info:disabled:hover { - background-color: #03a9f4; - border-color: #03a9f4 -} - -.bmd-btn-fab.btn-warning.custom-file-control:before, .btn-group-raised .btn-warning.custom-file-control:before, .btn-group-raised .btn.btn-warning, .btn-raised.btn-warning.custom-file-control:before, .btn.bmd-btn-fab.btn-warning, .btn.btn-raised.btn-warning { - color: #fff; - background-color: #ff5722; - border-color: #ff5722 -} - -.bmd-btn-fab.btn-warning.active.custom-file-control:before, .bmd-btn-fab.btn-warning.custom-file-control:active:before, .bmd-btn-fab.btn-warning.custom-file-control:focus:before, .bmd-btn-fab.btn-warning.custom-file-control:hover:before, .bmd-btn-fab.btn-warning.focus.custom-file-control:before, .btn-group-raised .btn-warning.active.custom-file-control:before, .btn-group-raised .btn-warning.custom-file-control:active:before, .btn-group-raised .btn-warning.custom-file-control:focus:before, .btn-group-raised .btn-warning.custom-file-control:hover:before, .btn-group-raised .btn-warning.focus.custom-file-control:before, .btn-group-raised .btn.btn-warning.active, .btn-group-raised .btn.btn-warning.focus, .btn-group-raised .btn.btn-warning:active, .btn-group-raised .btn.btn-warning:focus, .btn-group-raised .btn.btn-warning:hover, .btn-raised.btn-warning.active.custom-file-control:before, .btn-raised.btn-warning.custom-file-control:active:before, .btn-raised.btn-warning.custom-file-control:focus:before, .btn-raised.btn-warning.custom-file-control:hover:before, .btn-raised.btn-warning.focus.custom-file-control:before, .btn.bmd-btn-fab.btn-warning.active, .btn.bmd-btn-fab.btn-warning.focus, .btn.bmd-btn-fab.btn-warning:active, .btn.bmd-btn-fab.btn-warning:focus, .btn.bmd-btn-fab.btn-warning:hover, .btn.btn-raised.btn-warning.active, .btn.btn-raised.btn-warning.focus, .btn.btn-raised.btn-warning:active, .btn.btn-raised.btn-warning:focus, .btn.btn-raised.btn-warning:hover, .open > .bmd-btn-fab.btn-warning.dropdown-toggle.custom-file-control:before, .open > .btn-group-raised .btn-warning.dropdown-toggle.custom-file-control:before, .open > .btn-group-raised .btn.btn-warning.dropdown-toggle, .open > .btn-raised.btn-warning.dropdown-toggle.custom-file-control:before, .open > .btn.bmd-btn-fab.btn-warning.dropdown-toggle, .open > .btn.btn-raised.btn-warning.dropdown-toggle { - color: #fff; - background-color: #ff470e; - border-color: #e43700 -} - -.bmd-btn-fab.btn-warning.active.custom-file-control:focus:before, .bmd-btn-fab.btn-warning.active.custom-file-control:hover:before, .bmd-btn-fab.btn-warning.active.focus.custom-file-control:before, .bmd-btn-fab.btn-warning.custom-file-control:active.focus:before, .bmd-btn-fab.btn-warning.custom-file-control:active:focus:before, .bmd-btn-fab.btn-warning.custom-file-control:active:hover:before, .btn-group-raised .btn-warning.active.custom-file-control:focus:before, .btn-group-raised .btn-warning.active.custom-file-control:hover:before, .btn-group-raised .btn-warning.active.focus.custom-file-control:before, .btn-group-raised .btn-warning.custom-file-control:active.focus:before, .btn-group-raised .btn-warning.custom-file-control:active:focus:before, .btn-group-raised .btn-warning.custom-file-control:active:hover:before, .btn-group-raised .btn.btn-warning.active.focus, .btn-group-raised .btn.btn-warning.active:focus, .btn-group-raised .btn.btn-warning.active:hover, .btn-group-raised .btn.btn-warning:active.focus, .btn-group-raised .btn.btn-warning:active:focus, .btn-group-raised .btn.btn-warning:active:hover, .btn-raised.btn-warning.active.custom-file-control:focus:before, .btn-raised.btn-warning.active.custom-file-control:hover:before, .btn-raised.btn-warning.active.focus.custom-file-control:before, .btn-raised.btn-warning.custom-file-control:active.focus:before, .btn-raised.btn-warning.custom-file-control:active:focus:before, .btn-raised.btn-warning.custom-file-control:active:hover:before, .btn.bmd-btn-fab.btn-warning.active.focus, .btn.bmd-btn-fab.btn-warning.active:focus, .btn.bmd-btn-fab.btn-warning.active:hover, .btn.bmd-btn-fab.btn-warning:active.focus, .btn.bmd-btn-fab.btn-warning:active:focus, .btn.bmd-btn-fab.btn-warning:active:hover, .btn.btn-raised.btn-warning.active.focus, .btn.btn-raised.btn-warning.active:focus, .btn.btn-raised.btn-warning.active:hover, .btn.btn-raised.btn-warning:active.focus, .btn.btn-raised.btn-warning:active:focus, .btn.btn-raised.btn-warning:active:hover, .open > .bmd-btn-fab.btn-warning.dropdown-toggle.custom-file-control:focus:before, .open > .bmd-btn-fab.btn-warning.dropdown-toggle.custom-file-control:hover:before, .open > .bmd-btn-fab.btn-warning.dropdown-toggle.focus.custom-file-control:before, .open > .btn-group-raised .btn-warning.dropdown-toggle.custom-file-control:focus:before, .open > .btn-group-raised .btn-warning.dropdown-toggle.custom-file-control:hover:before, .open > .btn-group-raised .btn-warning.dropdown-toggle.focus.custom-file-control:before, .open > .btn-group-raised .btn.btn-warning.dropdown-toggle.focus, .open > .btn-group-raised .btn.btn-warning.dropdown-toggle:focus, .open > .btn-group-raised .btn.btn-warning.dropdown-toggle:hover, .open > .btn-raised.btn-warning.dropdown-toggle.custom-file-control:focus:before, .open > .btn-raised.btn-warning.dropdown-toggle.custom-file-control:hover:before, .open > .btn-raised.btn-warning.dropdown-toggle.focus.custom-file-control:before, .open > .btn.bmd-btn-fab.btn-warning.dropdown-toggle.focus, .open > .btn.bmd-btn-fab.btn-warning.dropdown-toggle:focus, .open > .btn.bmd-btn-fab.btn-warning.dropdown-toggle:hover, .open > .btn.btn-raised.btn-warning.dropdown-toggle.focus, .open > .btn.btn-raised.btn-warning.dropdown-toggle:focus, .open > .btn.btn-raised.btn-warning.dropdown-toggle:hover { - color: #fff; - background-color: #ff470e; - border-color: #a22700 -} - -.open > .bmd-btn-fab.btn-warning.dropdown-toggle.bmd-btn-icon.custom-file-control:before, .open > .btn-group-raised .btn-warning.dropdown-toggle.bmd-btn-icon.custom-file-control:before, .open > .btn-group-raised .btn.btn-warning.dropdown-toggle.bmd-btn-icon, .open > .btn-raised.btn-warning.dropdown-toggle.bmd-btn-icon.custom-file-control:before, .open > .btn.bmd-btn-fab.btn-warning.dropdown-toggle.bmd-btn-icon, .open > .btn.btn-raised.btn-warning.dropdown-toggle.bmd-btn-icon { - color: inherit; - background-color: #ff5722 -} - -.open > .bmd-btn-fab.btn-warning.dropdown-toggle.bmd-btn-icon.custom-file-control:hover:before, .open > .btn-group-raised .btn-warning.dropdown-toggle.bmd-btn-icon.custom-file-control:hover:before, .open > .btn-group-raised .btn.btn-warning.dropdown-toggle.bmd-btn-icon:hover, .open > .btn-raised.btn-warning.dropdown-toggle.bmd-btn-icon.custom-file-control:hover:before, .open > .btn.bmd-btn-fab.btn-warning.dropdown-toggle.bmd-btn-icon:hover, .open > .btn.btn-raised.btn-warning.dropdown-toggle.bmd-btn-icon:hover { - background-color: #ff470e -} - -.bmd-btn-fab.btn-warning.custom-file-control:disabled.focus:before, .bmd-btn-fab.btn-warning.custom-file-control:disabled:focus:before, .bmd-btn-fab.btn-warning.custom-file-control:disabled:hover:before, .bmd-btn-fab.btn-warning.disabled.custom-file-control:focus:before, .bmd-btn-fab.btn-warning.disabled.custom-file-control:hover:before, .bmd-btn-fab.btn-warning.disabled.focus.custom-file-control:before, .btn-group-raised .btn-warning.custom-file-control:disabled.focus:before, .btn-group-raised .btn-warning.custom-file-control:disabled:focus:before, .btn-group-raised .btn-warning.custom-file-control:disabled:hover:before, .btn-group-raised .btn-warning.disabled.custom-file-control:focus:before, .btn-group-raised .btn-warning.disabled.custom-file-control:hover:before, .btn-group-raised .btn-warning.disabled.focus.custom-file-control:before, .btn-group-raised .btn.btn-warning.disabled.focus, .btn-group-raised .btn.btn-warning.disabled:focus, .btn-group-raised .btn.btn-warning.disabled:hover, .btn-group-raised .btn.btn-warning:disabled.focus, .btn-group-raised .btn.btn-warning:disabled:focus, .btn-group-raised .btn.btn-warning:disabled:hover, .btn-raised.btn-warning.custom-file-control:disabled.focus:before, .btn-raised.btn-warning.custom-file-control:disabled:focus:before, .btn-raised.btn-warning.custom-file-control:disabled:hover:before, .btn-raised.btn-warning.disabled.custom-file-control:focus:before, .btn-raised.btn-warning.disabled.custom-file-control:hover:before, .btn-raised.btn-warning.disabled.focus.custom-file-control:before, .btn.bmd-btn-fab.btn-warning.disabled.focus, .btn.bmd-btn-fab.btn-warning.disabled:focus, .btn.bmd-btn-fab.btn-warning.disabled:hover, .btn.bmd-btn-fab.btn-warning:disabled.focus, .btn.bmd-btn-fab.btn-warning:disabled:focus, .btn.bmd-btn-fab.btn-warning:disabled:hover, .btn.btn-raised.btn-warning.disabled.focus, .btn.btn-raised.btn-warning.disabled:focus, .btn.btn-raised.btn-warning.disabled:hover, .btn.btn-raised.btn-warning:disabled.focus, .btn.btn-raised.btn-warning:disabled:focus, .btn.btn-raised.btn-warning:disabled:hover { - background-color: #ff5722; - border-color: #ff5722 -} - -.bmd-btn-fab.btn-danger.custom-file-control:before, .btn-group-raised .btn-danger.custom-file-control:before, .btn-group-raised .btn.btn-danger, .btn-raised.btn-danger.custom-file-control:before, .btn.bmd-btn-fab.btn-danger, .btn.btn-raised.btn-danger { - color: #fff; - background-color: #f44336; - border-color: #f44336 -} - -.bmd-btn-fab.btn-danger.active.custom-file-control:before, .bmd-btn-fab.btn-danger.custom-file-control:active:before, .bmd-btn-fab.btn-danger.custom-file-control:focus:before, .bmd-btn-fab.btn-danger.custom-file-control:hover:before, .bmd-btn-fab.btn-danger.focus.custom-file-control:before, .btn-group-raised .btn-danger.active.custom-file-control:before, .btn-group-raised .btn-danger.custom-file-control:active:before, .btn-group-raised .btn-danger.custom-file-control:focus:before, .btn-group-raised .btn-danger.custom-file-control:hover:before, .btn-group-raised .btn-danger.focus.custom-file-control:before, .btn-group-raised .btn.btn-danger.active, .btn-group-raised .btn.btn-danger.focus, .btn-group-raised .btn.btn-danger:active, .btn-group-raised .btn.btn-danger:focus, .btn-group-raised .btn.btn-danger:hover, .btn-raised.btn-danger.active.custom-file-control:before, .btn-raised.btn-danger.custom-file-control:active:before, .btn-raised.btn-danger.custom-file-control:focus:before, .btn-raised.btn-danger.custom-file-control:hover:before, .btn-raised.btn-danger.focus.custom-file-control:before, .btn.bmd-btn-fab.btn-danger.active, .btn.bmd-btn-fab.btn-danger.focus, .btn.bmd-btn-fab.btn-danger:active, .btn.bmd-btn-fab.btn-danger:focus, .btn.bmd-btn-fab.btn-danger:hover, .btn.btn-raised.btn-danger.active, .btn.btn-raised.btn-danger.focus, .btn.btn-raised.btn-danger:active, .btn.btn-raised.btn-danger:focus, .btn.btn-raised.btn-danger:hover, .open > .bmd-btn-fab.btn-danger.dropdown-toggle.custom-file-control:before, .open > .btn-group-raised .btn-danger.dropdown-toggle.custom-file-control:before, .open > .btn-group-raised .btn.btn-danger.dropdown-toggle, .open > .btn-raised.btn-danger.dropdown-toggle.custom-file-control:before, .open > .btn.bmd-btn-fab.btn-danger.dropdown-toggle, .open > .btn.btn-raised.btn-danger.dropdown-toggle { - color: #fff; - background-color: #f55549; - border-color: #e11b0c -} - -.bmd-btn-fab.btn-danger.active.custom-file-control:focus:before, .bmd-btn-fab.btn-danger.active.custom-file-control:hover:before, .bmd-btn-fab.btn-danger.active.focus.custom-file-control:before, .bmd-btn-fab.btn-danger.custom-file-control:active.focus:before, .bmd-btn-fab.btn-danger.custom-file-control:active:focus:before, .bmd-btn-fab.btn-danger.custom-file-control:active:hover:before, .btn-group-raised .btn-danger.active.custom-file-control:focus:before, .btn-group-raised .btn-danger.active.custom-file-control:hover:before, .btn-group-raised .btn-danger.active.focus.custom-file-control:before, .btn-group-raised .btn-danger.custom-file-control:active.focus:before, .btn-group-raised .btn-danger.custom-file-control:active:focus:before, .btn-group-raised .btn-danger.custom-file-control:active:hover:before, .btn-group-raised .btn.btn-danger.active.focus, .btn-group-raised .btn.btn-danger.active:focus, .btn-group-raised .btn.btn-danger.active:hover, .btn-group-raised .btn.btn-danger:active.focus, .btn-group-raised .btn.btn-danger:active:focus, .btn-group-raised .btn.btn-danger:active:hover, .btn-raised.btn-danger.active.custom-file-control:focus:before, .btn-raised.btn-danger.active.custom-file-control:hover:before, .btn-raised.btn-danger.active.focus.custom-file-control:before, .btn-raised.btn-danger.custom-file-control:active.focus:before, .btn-raised.btn-danger.custom-file-control:active:focus:before, .btn-raised.btn-danger.custom-file-control:active:hover:before, .btn.bmd-btn-fab.btn-danger.active.focus, .btn.bmd-btn-fab.btn-danger.active:focus, .btn.bmd-btn-fab.btn-danger.active:hover, .btn.bmd-btn-fab.btn-danger:active.focus, .btn.bmd-btn-fab.btn-danger:active:focus, .btn.bmd-btn-fab.btn-danger:active:hover, .btn.btn-raised.btn-danger.active.focus, .btn.btn-raised.btn-danger.active:focus, .btn.btn-raised.btn-danger.active:hover, .btn.btn-raised.btn-danger:active.focus, .btn.btn-raised.btn-danger:active:focus, .btn.btn-raised.btn-danger:active:hover, .open > .bmd-btn-fab.btn-danger.dropdown-toggle.custom-file-control:focus:before, .open > .bmd-btn-fab.btn-danger.dropdown-toggle.custom-file-control:hover:before, .open > .bmd-btn-fab.btn-danger.dropdown-toggle.focus.custom-file-control:before, .open > .btn-group-raised .btn-danger.dropdown-toggle.custom-file-control:focus:before, .open > .btn-group-raised .btn-danger.dropdown-toggle.custom-file-control:hover:before, .open > .btn-group-raised .btn-danger.dropdown-toggle.focus.custom-file-control:before, .open > .btn-group-raised .btn.btn-danger.dropdown-toggle.focus, .open > .btn-group-raised .btn.btn-danger.dropdown-toggle:focus, .open > .btn-group-raised .btn.btn-danger.dropdown-toggle:hover, .open > .btn-raised.btn-danger.dropdown-toggle.custom-file-control:focus:before, .open > .btn-raised.btn-danger.dropdown-toggle.custom-file-control:hover:before, .open > .btn-raised.btn-danger.dropdown-toggle.focus.custom-file-control:before, .open > .btn.bmd-btn-fab.btn-danger.dropdown-toggle.focus, .open > .btn.bmd-btn-fab.btn-danger.dropdown-toggle:focus, .open > .btn.bmd-btn-fab.btn-danger.dropdown-toggle:hover, .open > .btn.btn-raised.btn-danger.dropdown-toggle.focus, .open > .btn.btn-raised.btn-danger.dropdown-toggle:focus, .open > .btn.btn-raised.btn-danger.dropdown-toggle:hover { - color: #fff; - background-color: #f55549; - border-color: #a21309 -} - -.open > .bmd-btn-fab.btn-danger.dropdown-toggle.bmd-btn-icon.custom-file-control:before, .open > .btn-group-raised .btn-danger.dropdown-toggle.bmd-btn-icon.custom-file-control:before, .open > .btn-group-raised .btn.btn-danger.dropdown-toggle.bmd-btn-icon, .open > .btn-raised.btn-danger.dropdown-toggle.bmd-btn-icon.custom-file-control:before, .open > .btn.bmd-btn-fab.btn-danger.dropdown-toggle.bmd-btn-icon, .open > .btn.btn-raised.btn-danger.dropdown-toggle.bmd-btn-icon { - color: inherit; - background-color: #f44336 -} - -.open > .bmd-btn-fab.btn-danger.dropdown-toggle.bmd-btn-icon.custom-file-control:hover:before, .open > .btn-group-raised .btn-danger.dropdown-toggle.bmd-btn-icon.custom-file-control:hover:before, .open > .btn-group-raised .btn.btn-danger.dropdown-toggle.bmd-btn-icon:hover, .open > .btn-raised.btn-danger.dropdown-toggle.bmd-btn-icon.custom-file-control:hover:before, .open > .btn.bmd-btn-fab.btn-danger.dropdown-toggle.bmd-btn-icon:hover, .open > .btn.btn-raised.btn-danger.dropdown-toggle.bmd-btn-icon:hover { - background-color: #f55549 -} - -.bmd-btn-fab.btn-danger.custom-file-control:disabled.focus:before, .bmd-btn-fab.btn-danger.custom-file-control:disabled:focus:before, .bmd-btn-fab.btn-danger.custom-file-control:disabled:hover:before, .bmd-btn-fab.btn-danger.disabled.custom-file-control:focus:before, .bmd-btn-fab.btn-danger.disabled.custom-file-control:hover:before, .bmd-btn-fab.btn-danger.disabled.focus.custom-file-control:before, .btn-group-raised .btn-danger.custom-file-control:disabled.focus:before, .btn-group-raised .btn-danger.custom-file-control:disabled:focus:before, .btn-group-raised .btn-danger.custom-file-control:disabled:hover:before, .btn-group-raised .btn-danger.disabled.custom-file-control:focus:before, .btn-group-raised .btn-danger.disabled.custom-file-control:hover:before, .btn-group-raised .btn-danger.disabled.focus.custom-file-control:before, .btn-group-raised .btn.btn-danger.disabled.focus, .btn-group-raised .btn.btn-danger.disabled:focus, .btn-group-raised .btn.btn-danger.disabled:hover, .btn-group-raised .btn.btn-danger:disabled.focus, .btn-group-raised .btn.btn-danger:disabled:focus, .btn-group-raised .btn.btn-danger:disabled:hover, .btn-raised.btn-danger.custom-file-control:disabled.focus:before, .btn-raised.btn-danger.custom-file-control:disabled:focus:before, .btn-raised.btn-danger.custom-file-control:disabled:hover:before, .btn-raised.btn-danger.disabled.custom-file-control:focus:before, .btn-raised.btn-danger.disabled.custom-file-control:hover:before, .btn-raised.btn-danger.disabled.focus.custom-file-control:before, .btn.bmd-btn-fab.btn-danger.disabled.focus, .btn.bmd-btn-fab.btn-danger.disabled:focus, .btn.bmd-btn-fab.btn-danger.disabled:hover, .btn.bmd-btn-fab.btn-danger:disabled.focus, .btn.bmd-btn-fab.btn-danger:disabled:focus, .btn.bmd-btn-fab.btn-danger:disabled:hover, .btn.btn-raised.btn-danger.disabled.focus, .btn.btn-raised.btn-danger.disabled:focus, .btn.btn-raised.btn-danger.disabled:hover, .btn.btn-raised.btn-danger:disabled.focus, .btn.btn-raised.btn-danger:disabled:focus, .btn.btn-raised.btn-danger:disabled:hover { - background-color: #f44336; - border-color: #f44336 -} - -.bmd-btn-fab.btn-light.custom-file-control:before, .btn-group-raised .btn-light.custom-file-control:before, .btn-group-raised .btn.btn-light, .btn-raised.btn-light.custom-file-control:before, .btn.bmd-btn-fab.btn-light, .btn.btn-raised.btn-light { - color: #fff; - background-color: #f5f5f5; - border-color: #f5f5f5 -} - -.bmd-btn-fab.btn-light.active.custom-file-control:before, .bmd-btn-fab.btn-light.custom-file-control:active:before, .bmd-btn-fab.btn-light.custom-file-control:focus:before, .bmd-btn-fab.btn-light.custom-file-control:hover:before, .bmd-btn-fab.btn-light.focus.custom-file-control:before, .btn-group-raised .btn-light.active.custom-file-control:before, .btn-group-raised .btn-light.custom-file-control:active:before, .btn-group-raised .btn-light.custom-file-control:focus:before, .btn-group-raised .btn-light.custom-file-control:hover:before, .btn-group-raised .btn-light.focus.custom-file-control:before, .btn-group-raised .btn.btn-light.active, .btn-group-raised .btn.btn-light.focus, .btn-group-raised .btn.btn-light:active, .btn-group-raised .btn.btn-light:focus, .btn-group-raised .btn.btn-light:hover, .btn-raised.btn-light.active.custom-file-control:before, .btn-raised.btn-light.custom-file-control:active:before, .btn-raised.btn-light.custom-file-control:focus:before, .btn-raised.btn-light.custom-file-control:hover:before, .btn-raised.btn-light.focus.custom-file-control:before, .btn.bmd-btn-fab.btn-light.active, .btn.bmd-btn-fab.btn-light.focus, .btn.bmd-btn-fab.btn-light:active, .btn.bmd-btn-fab.btn-light:focus, .btn.bmd-btn-fab.btn-light:hover, .btn.btn-raised.btn-light.active, .btn.btn-raised.btn-light.focus, .btn.btn-raised.btn-light:active, .btn.btn-raised.btn-light:focus, .btn.btn-raised.btn-light:hover, .open > .bmd-btn-fab.btn-light.dropdown-toggle.custom-file-control:before, .open > .btn-group-raised .btn-light.dropdown-toggle.custom-file-control:before, .open > .btn-group-raised .btn.btn-light.dropdown-toggle, .open > .btn-raised.btn-light.dropdown-toggle.custom-file-control:before, .open > .btn.bmd-btn-fab.btn-light.dropdown-toggle, .open > .btn.btn-raised.btn-light.dropdown-toggle { - color: #fff; - background-color: #ebebeb; - border-color: #d6d6d6 -} - -.bmd-btn-fab.btn-light.active.custom-file-control:focus:before, .bmd-btn-fab.btn-light.active.custom-file-control:hover:before, .bmd-btn-fab.btn-light.active.focus.custom-file-control:before, .bmd-btn-fab.btn-light.custom-file-control:active.focus:before, .bmd-btn-fab.btn-light.custom-file-control:active:focus:before, .bmd-btn-fab.btn-light.custom-file-control:active:hover:before, .btn-group-raised .btn-light.active.custom-file-control:focus:before, .btn-group-raised .btn-light.active.custom-file-control:hover:before, .btn-group-raised .btn-light.active.focus.custom-file-control:before, .btn-group-raised .btn-light.custom-file-control:active.focus:before, .btn-group-raised .btn-light.custom-file-control:active:focus:before, .btn-group-raised .btn-light.custom-file-control:active:hover:before, .btn-group-raised .btn.btn-light.active.focus, .btn-group-raised .btn.btn-light.active:focus, .btn-group-raised .btn.btn-light.active:hover, .btn-group-raised .btn.btn-light:active.focus, .btn-group-raised .btn.btn-light:active:focus, .btn-group-raised .btn.btn-light:active:hover, .btn-raised.btn-light.active.custom-file-control:focus:before, .btn-raised.btn-light.active.custom-file-control:hover:before, .btn-raised.btn-light.active.focus.custom-file-control:before, .btn-raised.btn-light.custom-file-control:active.focus:before, .btn-raised.btn-light.custom-file-control:active:focus:before, .btn-raised.btn-light.custom-file-control:active:hover:before, .btn.bmd-btn-fab.btn-light.active.focus, .btn.bmd-btn-fab.btn-light.active:focus, .btn.bmd-btn-fab.btn-light.active:hover, .btn.bmd-btn-fab.btn-light:active.focus, .btn.bmd-btn-fab.btn-light:active:focus, .btn.bmd-btn-fab.btn-light:active:hover, .btn.btn-raised.btn-light.active.focus, .btn.btn-raised.btn-light.active:focus, .btn.btn-raised.btn-light.active:hover, .btn.btn-raised.btn-light:active.focus, .btn.btn-raised.btn-light:active:focus, .btn.btn-raised.btn-light:active:hover, .open > .bmd-btn-fab.btn-light.dropdown-toggle.custom-file-control:focus:before, .open > .bmd-btn-fab.btn-light.dropdown-toggle.custom-file-control:hover:before, .open > .bmd-btn-fab.btn-light.dropdown-toggle.focus.custom-file-control:before, .open > .btn-group-raised .btn-light.dropdown-toggle.custom-file-control:focus:before, .open > .btn-group-raised .btn-light.dropdown-toggle.custom-file-control:hover:before, .open > .btn-group-raised .btn-light.dropdown-toggle.focus.custom-file-control:before, .open > .btn-group-raised .btn.btn-light.dropdown-toggle.focus, .open > .btn-group-raised .btn.btn-light.dropdown-toggle:focus, .open > .btn-group-raised .btn.btn-light.dropdown-toggle:hover, .open > .btn-raised.btn-light.dropdown-toggle.custom-file-control:focus:before, .open > .btn-raised.btn-light.dropdown-toggle.custom-file-control:hover:before, .open > .btn-raised.btn-light.dropdown-toggle.focus.custom-file-control:before, .open > .btn.bmd-btn-fab.btn-light.dropdown-toggle.focus, .open > .btn.bmd-btn-fab.btn-light.dropdown-toggle:focus, .open > .btn.bmd-btn-fab.btn-light.dropdown-toggle:hover, .open > .btn.btn-raised.btn-light.dropdown-toggle.focus, .open > .btn.btn-raised.btn-light.dropdown-toggle:focus, .open > .btn.btn-raised.btn-light.dropdown-toggle:hover { - color: #fff; - background-color: #ebebeb; - border-color: #b5b5b5 -} - -.open > .bmd-btn-fab.btn-light.dropdown-toggle.bmd-btn-icon.custom-file-control:before, .open > .btn-group-raised .btn-light.dropdown-toggle.bmd-btn-icon.custom-file-control:before, .open > .btn-group-raised .btn.btn-light.dropdown-toggle.bmd-btn-icon, .open > .btn-raised.btn-light.dropdown-toggle.bmd-btn-icon.custom-file-control:before, .open > .btn.bmd-btn-fab.btn-light.dropdown-toggle.bmd-btn-icon, .open > .btn.btn-raised.btn-light.dropdown-toggle.bmd-btn-icon { - color: inherit; - background-color: #f5f5f5 -} - -.open > .bmd-btn-fab.btn-light.dropdown-toggle.bmd-btn-icon.custom-file-control:hover:before, .open > .btn-group-raised .btn-light.dropdown-toggle.bmd-btn-icon.custom-file-control:hover:before, .open > .btn-group-raised .btn.btn-light.dropdown-toggle.bmd-btn-icon:hover, .open > .btn-raised.btn-light.dropdown-toggle.bmd-btn-icon.custom-file-control:hover:before, .open > .btn.bmd-btn-fab.btn-light.dropdown-toggle.bmd-btn-icon:hover, .open > .btn.btn-raised.btn-light.dropdown-toggle.bmd-btn-icon:hover { - background-color: #ebebeb -} - -.bmd-btn-fab.btn-light.custom-file-control:disabled.focus:before, .bmd-btn-fab.btn-light.custom-file-control:disabled:focus:before, .bmd-btn-fab.btn-light.custom-file-control:disabled:hover:before, .bmd-btn-fab.btn-light.disabled.custom-file-control:focus:before, .bmd-btn-fab.btn-light.disabled.custom-file-control:hover:before, .bmd-btn-fab.btn-light.disabled.focus.custom-file-control:before, .btn-group-raised .btn-light.custom-file-control:disabled.focus:before, .btn-group-raised .btn-light.custom-file-control:disabled:focus:before, .btn-group-raised .btn-light.custom-file-control:disabled:hover:before, .btn-group-raised .btn-light.disabled.custom-file-control:focus:before, .btn-group-raised .btn-light.disabled.custom-file-control:hover:before, .btn-group-raised .btn-light.disabled.focus.custom-file-control:before, .btn-group-raised .btn.btn-light.disabled.focus, .btn-group-raised .btn.btn-light.disabled:focus, .btn-group-raised .btn.btn-light.disabled:hover, .btn-group-raised .btn.btn-light:disabled.focus, .btn-group-raised .btn.btn-light:disabled:focus, .btn-group-raised .btn.btn-light:disabled:hover, .btn-raised.btn-light.custom-file-control:disabled.focus:before, .btn-raised.btn-light.custom-file-control:disabled:focus:before, .btn-raised.btn-light.custom-file-control:disabled:hover:before, .btn-raised.btn-light.disabled.custom-file-control:focus:before, .btn-raised.btn-light.disabled.custom-file-control:hover:before, .btn-raised.btn-light.disabled.focus.custom-file-control:before, .btn.bmd-btn-fab.btn-light.disabled.focus, .btn.bmd-btn-fab.btn-light.disabled:focus, .btn.bmd-btn-fab.btn-light.disabled:hover, .btn.bmd-btn-fab.btn-light:disabled.focus, .btn.bmd-btn-fab.btn-light:disabled:focus, .btn.bmd-btn-fab.btn-light:disabled:hover, .btn.btn-raised.btn-light.disabled.focus, .btn.btn-raised.btn-light.disabled:focus, .btn.btn-raised.btn-light.disabled:hover, .btn.btn-raised.btn-light:disabled.focus, .btn.btn-raised.btn-light:disabled:focus, .btn.btn-raised.btn-light:disabled:hover { - background-color: #f5f5f5; - border-color: #f5f5f5 -} - -.bmd-btn-fab.btn-dark.custom-file-control:before, .btn-group-raised .btn-dark.custom-file-control:before, .btn-group-raised .btn.btn-dark, .btn-raised.btn-dark.custom-file-control:before, .btn.bmd-btn-fab.btn-dark, .btn.btn-raised.btn-dark { - color: #fff; - background-color: #424242; - border-color: #424242 -} - -.bmd-btn-fab.btn-dark.active.custom-file-control:before, .bmd-btn-fab.btn-dark.custom-file-control:active:before, .bmd-btn-fab.btn-dark.custom-file-control:focus:before, .bmd-btn-fab.btn-dark.custom-file-control:hover:before, .bmd-btn-fab.btn-dark.focus.custom-file-control:before, .btn-group-raised .btn-dark.active.custom-file-control:before, .btn-group-raised .btn-dark.custom-file-control:active:before, .btn-group-raised .btn-dark.custom-file-control:focus:before, .btn-group-raised .btn-dark.custom-file-control:hover:before, .btn-group-raised .btn-dark.focus.custom-file-control:before, .btn-group-raised .btn.btn-dark.active, .btn-group-raised .btn.btn-dark.focus, .btn-group-raised .btn.btn-dark:active, .btn-group-raised .btn.btn-dark:focus, .btn-group-raised .btn.btn-dark:hover, .btn-raised.btn-dark.active.custom-file-control:before, .btn-raised.btn-dark.custom-file-control:active:before, .btn-raised.btn-dark.custom-file-control:focus:before, .btn-raised.btn-dark.custom-file-control:hover:before, .btn-raised.btn-dark.focus.custom-file-control:before, .btn.bmd-btn-fab.btn-dark.active, .btn.bmd-btn-fab.btn-dark.focus, .btn.bmd-btn-fab.btn-dark:active, .btn.bmd-btn-fab.btn-dark:focus, .btn.bmd-btn-fab.btn-dark:hover, .btn.btn-raised.btn-dark.active, .btn.btn-raised.btn-dark.focus, .btn.btn-raised.btn-dark:active, .btn.btn-raised.btn-dark:focus, .btn.btn-raised.btn-dark:hover, .open > .bmd-btn-fab.btn-dark.dropdown-toggle.custom-file-control:before, .open > .btn-group-raised .btn-dark.dropdown-toggle.custom-file-control:before, .open > .btn-group-raised .btn.btn-dark.dropdown-toggle, .open > .btn-raised.btn-dark.dropdown-toggle.custom-file-control:before, .open > .btn.bmd-btn-fab.btn-dark.dropdown-toggle, .open > .btn.btn-raised.btn-dark.dropdown-toggle { - color: #fff; - background-color: #383838; - border-color: #232323 -} - -.bmd-btn-fab.btn-dark.active.custom-file-control:focus:before, .bmd-btn-fab.btn-dark.active.custom-file-control:hover:before, .bmd-btn-fab.btn-dark.active.focus.custom-file-control:before, .bmd-btn-fab.btn-dark.custom-file-control:active.focus:before, .bmd-btn-fab.btn-dark.custom-file-control:active:focus:before, .bmd-btn-fab.btn-dark.custom-file-control:active:hover:before, .btn-group-raised .btn-dark.active.custom-file-control:focus:before, .btn-group-raised .btn-dark.active.custom-file-control:hover:before, .btn-group-raised .btn-dark.active.focus.custom-file-control:before, .btn-group-raised .btn-dark.custom-file-control:active.focus:before, .btn-group-raised .btn-dark.custom-file-control:active:focus:before, .btn-group-raised .btn-dark.custom-file-control:active:hover:before, .btn-group-raised .btn.btn-dark.active.focus, .btn-group-raised .btn.btn-dark.active:focus, .btn-group-raised .btn.btn-dark.active:hover, .btn-group-raised .btn.btn-dark:active.focus, .btn-group-raised .btn.btn-dark:active:focus, .btn-group-raised .btn.btn-dark:active:hover, .btn-raised.btn-dark.active.custom-file-control:focus:before, .btn-raised.btn-dark.active.custom-file-control:hover:before, .btn-raised.btn-dark.active.focus.custom-file-control:before, .btn-raised.btn-dark.custom-file-control:active.focus:before, .btn-raised.btn-dark.custom-file-control:active:focus:before, .btn-raised.btn-dark.custom-file-control:active:hover:before, .btn.bmd-btn-fab.btn-dark.active.focus, .btn.bmd-btn-fab.btn-dark.active:focus, .btn.bmd-btn-fab.btn-dark.active:hover, .btn.bmd-btn-fab.btn-dark:active.focus, .btn.bmd-btn-fab.btn-dark:active:focus, .btn.bmd-btn-fab.btn-dark:active:hover, .btn.btn-raised.btn-dark.active.focus, .btn.btn-raised.btn-dark.active:focus, .btn.btn-raised.btn-dark.active:hover, .btn.btn-raised.btn-dark:active.focus, .btn.btn-raised.btn-dark:active:focus, .btn.btn-raised.btn-dark:active:hover, .open > .bmd-btn-fab.btn-dark.dropdown-toggle.custom-file-control:focus:before, .open > .bmd-btn-fab.btn-dark.dropdown-toggle.custom-file-control:hover:before, .open > .bmd-btn-fab.btn-dark.dropdown-toggle.focus.custom-file-control:before, .open > .btn-group-raised .btn-dark.dropdown-toggle.custom-file-control:focus:before, .open > .btn-group-raised .btn-dark.dropdown-toggle.custom-file-control:hover:before, .open > .btn-group-raised .btn-dark.dropdown-toggle.focus.custom-file-control:before, .open > .btn-group-raised .btn.btn-dark.dropdown-toggle.focus, .open > .btn-group-raised .btn.btn-dark.dropdown-toggle:focus, .open > .btn-group-raised .btn.btn-dark.dropdown-toggle:hover, .open > .btn-raised.btn-dark.dropdown-toggle.custom-file-control:focus:before, .open > .btn-raised.btn-dark.dropdown-toggle.custom-file-control:hover:before, .open > .btn-raised.btn-dark.dropdown-toggle.focus.custom-file-control:before, .open > .btn.bmd-btn-fab.btn-dark.dropdown-toggle.focus, .open > .btn.bmd-btn-fab.btn-dark.dropdown-toggle:focus, .open > .btn.bmd-btn-fab.btn-dark.dropdown-toggle:hover, .open > .btn.btn-raised.btn-dark.dropdown-toggle.focus, .open > .btn.btn-raised.btn-dark.dropdown-toggle:focus, .open > .btn.btn-raised.btn-dark.dropdown-toggle:hover { - color: #fff; - background-color: #383838; - border-color: #020202 -} - -.open > .bmd-btn-fab.btn-dark.dropdown-toggle.bmd-btn-icon.custom-file-control:before, .open > .btn-group-raised .btn-dark.dropdown-toggle.bmd-btn-icon.custom-file-control:before, .open > .btn-group-raised .btn.btn-dark.dropdown-toggle.bmd-btn-icon, .open > .btn-raised.btn-dark.dropdown-toggle.bmd-btn-icon.custom-file-control:before, .open > .btn.bmd-btn-fab.btn-dark.dropdown-toggle.bmd-btn-icon, .open > .btn.btn-raised.btn-dark.dropdown-toggle.bmd-btn-icon { - color: inherit; - background-color: #424242 -} - -.open > .bmd-btn-fab.btn-dark.dropdown-toggle.bmd-btn-icon.custom-file-control:hover:before, .open > .btn-group-raised .btn-dark.dropdown-toggle.bmd-btn-icon.custom-file-control:hover:before, .open > .btn-group-raised .btn.btn-dark.dropdown-toggle.bmd-btn-icon:hover, .open > .btn-raised.btn-dark.dropdown-toggle.bmd-btn-icon.custom-file-control:hover:before, .open > .btn.bmd-btn-fab.btn-dark.dropdown-toggle.bmd-btn-icon:hover, .open > .btn.btn-raised.btn-dark.dropdown-toggle.bmd-btn-icon:hover { - background-color: #383838 -} - -.bmd-btn-fab.btn-dark.custom-file-control:disabled.focus:before, .bmd-btn-fab.btn-dark.custom-file-control:disabled:focus:before, .bmd-btn-fab.btn-dark.custom-file-control:disabled:hover:before, .bmd-btn-fab.btn-dark.disabled.custom-file-control:focus:before, .bmd-btn-fab.btn-dark.disabled.custom-file-control:hover:before, .bmd-btn-fab.btn-dark.disabled.focus.custom-file-control:before, .btn-group-raised .btn-dark.custom-file-control:disabled.focus:before, .btn-group-raised .btn-dark.custom-file-control:disabled:focus:before, .btn-group-raised .btn-dark.custom-file-control:disabled:hover:before, .btn-group-raised .btn-dark.disabled.custom-file-control:focus:before, .btn-group-raised .btn-dark.disabled.custom-file-control:hover:before, .btn-group-raised .btn-dark.disabled.focus.custom-file-control:before, .btn-group-raised .btn.btn-dark.disabled.focus, .btn-group-raised .btn.btn-dark.disabled:focus, .btn-group-raised .btn.btn-dark.disabled:hover, .btn-group-raised .btn.btn-dark:disabled.focus, .btn-group-raised .btn.btn-dark:disabled:focus, .btn-group-raised .btn.btn-dark:disabled:hover, .btn-raised.btn-dark.custom-file-control:disabled.focus:before, .btn-raised.btn-dark.custom-file-control:disabled:focus:before, .btn-raised.btn-dark.custom-file-control:disabled:hover:before, .btn-raised.btn-dark.disabled.custom-file-control:focus:before, .btn-raised.btn-dark.disabled.custom-file-control:hover:before, .btn-raised.btn-dark.disabled.focus.custom-file-control:before, .btn.bmd-btn-fab.btn-dark.disabled.focus, .btn.bmd-btn-fab.btn-dark.disabled:focus, .btn.bmd-btn-fab.btn-dark.disabled:hover, .btn.bmd-btn-fab.btn-dark:disabled.focus, .btn.bmd-btn-fab.btn-dark:disabled:focus, .btn.bmd-btn-fab.btn-dark:disabled:hover, .btn.btn-raised.btn-dark.disabled.focus, .btn.btn-raised.btn-dark.disabled:focus, .btn.btn-raised.btn-dark.disabled:hover, .btn.btn-raised.btn-dark:disabled.focus, .btn.btn-raised.btn-dark:disabled:focus, .btn.btn-raised.btn-dark:disabled:hover { - background-color: #424242; - border-color: #424242 -} - -.bmd-btn-fab.custom-file-control:focus:before, .bmd-btn-fab.custom-file-control:hover:before, .btn-group-raised .btn:focus, .btn-group-raised .btn:hover, .btn-group-raised .custom-file-control:focus:before, .btn-group-raised .custom-file-control:hover:before, .btn-raised.custom-file-control:focus:before, .btn-raised.custom-file-control:hover:before, .btn.bmd-btn-fab:focus, .btn.bmd-btn-fab:hover, .btn.btn-raised:focus, .btn.btn-raised:hover { - z-index: 1; - box-shadow: 0 4px 5px 0 rgba(0, 0, 0, .14), 0 1px 10px 0 rgba(0, 0, 0, .12), 0 2px 4px -1px rgba(0, 0, 0, .2) -} - -.bmd-btn-fab.active.custom-file-control:before, .bmd-btn-fab.custom-file-control:active:before, .btn-group-raised .active.custom-file-control:before, .btn-group-raised .btn.active, .btn-group-raised .btn:active, .btn-group-raised .custom-file-control:active:before, .btn-raised.active.custom-file-control:before, .btn-raised.custom-file-control:active:before, .btn.bmd-btn-fab.active, .btn.bmd-btn-fab:active, .btn.btn-raised.active, .btn.btn-raised:active { - z-index: 1; - box-shadow: 0 0 8px rgba(0, 0, 0, .18), 0 8px 16px rgba(0, 0, 0, .36) -} - -.bmd-btn-fab.custom-file-control:focus:before, .bmd-btn-fab.focus.custom-file-control:before, .btn-group-raised .btn.focus, .btn-group-raised .btn:focus, .btn-group-raised .custom-file-control:focus:before, .btn-group-raised .focus.custom-file-control:before, .btn-raised.custom-file-control:focus:before, .btn-raised.focus.custom-file-control:before, .btn.bmd-btn-fab.focus, .btn.bmd-btn-fab:focus, .btn.btn-raised.focus, .btn.btn-raised:focus { - outline: 0 -} - -.btn-group-raised .btn, .btn-group-raised .custom-file-control:before, .btn-raised.custom-file-control:before, .btn.btn-raised { - box-shadow: 0 2px 2px 0 rgba(0, 0, 0, .14), 0 3px 1px -2px rgba(0, 0, 0, .2), 0 1px 5px 0 rgba(0, 0, 0, .12) -} - -.btn-group-raised .btn-link.active.custom-file-control:before, .btn-group-raised .btn-link.custom-file-control:active:before, .btn-group-raised .btn-link.custom-file-control:before, .btn-group-raised .btn-link.custom-file-control:focus:before, .btn-group-raised .btn-link.custom-file-control:hover:before, .btn-group-raised .btn.btn-link, .btn-group-raised .btn.btn-link.active, .btn-group-raised .btn.btn-link:active, .btn-group-raised .btn.btn-link:focus, .btn-group-raised .btn.btn-link:hover, .btn-group-raised .btn.disabled, .btn-group-raised .btn:disabled, .btn-group-raised .btn[disabled], .btn-group-raised .custom-file-control:disabled:before, .btn-group-raised .disabled.custom-file-control:before, .btn-group-raised [disabled].custom-file-control:before, .btn-raised.btn-link.active.custom-file-control:before, .btn-raised.btn-link.custom-file-control:active:before, .btn-raised.btn-link.custom-file-control:before, .btn-raised.btn-link.custom-file-control:focus:before, .btn-raised.btn-link.custom-file-control:hover:before, .btn-raised.custom-file-control:disabled:before, .btn-raised.disabled.custom-file-control:before, .btn-raised[disabled].custom-file-control:before, .btn.btn-raised.btn-link, .btn.btn-raised.btn-link.active, .btn.btn-raised.btn-link:active, .btn.btn-raised.btn-link:focus, .btn.btn-raised.btn-link:hover, .btn.btn-raised.disabled, .btn.btn-raised:disabled, .btn.btn-raised[disabled], fieldset[disabled][disabled] .btn-group-raised .btn, fieldset[disabled][disabled] .btn-group-raised .custom-file-control:before, fieldset[disabled][disabled] .btn-raised.custom-file-control:before, fieldset[disabled][disabled] .btn.btn-raised { - box-shadow: none -} - -.btn-outline.custom-file-control:before, .btn.btn-outline { - border: 1px solid currentColor -} - -.btn-outline-primary.custom-file-control:before, .btn.btn-outline-primary { - border-color: currentColor; - color: #009688; - background-color: transparent; - border: 1px solid #009688 -} - -.btn-outline-primary.active.custom-file-control:before, .btn-outline-primary.custom-file-control:active:before, .btn-outline-primary.custom-file-control:focus:before, .btn-outline-primary.custom-file-control:hover:before, .btn-outline-primary.focus.custom-file-control:before, .btn.btn-outline-primary.active, .btn.btn-outline-primary.focus, .btn.btn-outline-primary:active, .btn.btn-outline-primary:focus, .btn.btn-outline-primary:hover, .open > .btn-outline-primary.dropdown-toggle.custom-file-control:before, .open > .btn.btn-outline-primary.dropdown-toggle { - color: #009688; - background-color: hsla(0, 0%, 60%, .2); - border-color: #009688 -} - -.btn-outline-primary.active.custom-file-control:focus:before, .btn-outline-primary.active.custom-file-control:hover:before, .btn-outline-primary.active.focus.custom-file-control:before, .btn-outline-primary.custom-file-control:active.focus:before, .btn-outline-primary.custom-file-control:active:focus:before, .btn-outline-primary.custom-file-control:active:hover:before, .btn.btn-outline-primary.active.focus, .btn.btn-outline-primary.active:focus, .btn.btn-outline-primary.active:hover, .btn.btn-outline-primary:active.focus, .btn.btn-outline-primary:active:focus, .btn.btn-outline-primary:active:hover, .open > .btn-outline-primary.dropdown-toggle.custom-file-control:focus:before, .open > .btn-outline-primary.dropdown-toggle.custom-file-control:hover:before, .open > .btn-outline-primary.dropdown-toggle.focus.custom-file-control:before, .open > .btn.btn-outline-primary.dropdown-toggle.focus, .open > .btn.btn-outline-primary.dropdown-toggle:focus, .open > .btn.btn-outline-primary.dropdown-toggle:hover { - color: #009688; - background-color: hsla(0, 0%, 60%, .4); - border-color: #009688 -} - -.open > .btn-outline-primary.dropdown-toggle.bmd-btn-icon.custom-file-control:before, .open > .btn.btn-outline-primary.dropdown-toggle.bmd-btn-icon { - color: inherit; - background-color: transparent -} - -.open > .btn-outline-primary.dropdown-toggle.bmd-btn-icon.custom-file-control:hover:before, .open > .btn.btn-outline-primary.dropdown-toggle.bmd-btn-icon:hover { - background-color: hsla(0, 0%, 60%, .2) -} - -.btn-outline-primary.custom-file-control:disabled.focus:before, .btn-outline-primary.custom-file-control:disabled:focus:before, .btn-outline-primary.custom-file-control:disabled:hover:before, .btn-outline-primary.disabled.custom-file-control:focus:before, .btn-outline-primary.disabled.custom-file-control:hover:before, .btn-outline-primary.disabled.focus.custom-file-control:before, .btn.btn-outline-primary.disabled.focus, .btn.btn-outline-primary.disabled:focus, .btn.btn-outline-primary.disabled:hover, .btn.btn-outline-primary:disabled.focus, .btn.btn-outline-primary:disabled:focus, .btn.btn-outline-primary:disabled:hover { - background-color: transparent; - border-color: #009688 -} - -.bg-inverse .btn-outline-primary.custom-file-control:before, .bg-inverse .btn.btn-outline-primary { - color: #009688; - background-color: transparent; - border-color: #009688 -} - -.bg-inverse .btn-outline-primary.active.custom-file-control:before, .bg-inverse .btn-outline-primary.custom-file-control:active:before, .bg-inverse .btn-outline-primary.custom-file-control:focus:before, .bg-inverse .btn-outline-primary.custom-file-control:hover:before, .bg-inverse .btn-outline-primary.focus.custom-file-control:before, .bg-inverse .btn.btn-outline-primary.active, .bg-inverse .btn.btn-outline-primary.focus, .bg-inverse .btn.btn-outline-primary:active, .bg-inverse .btn.btn-outline-primary:focus, .bg-inverse .btn.btn-outline-primary:hover, .open > .bg-inverse .btn-outline-primary.dropdown-toggle.custom-file-control:before, .open > .bg-inverse .btn.btn-outline-primary.dropdown-toggle { - color: #009688; - background-color: hsla(0, 0%, 80%, .15); - border-color: hsla(0, 0%, 80%, .15) -} - -.bg-inverse .btn-outline-primary.active.custom-file-control:focus:before, .bg-inverse .btn-outline-primary.active.custom-file-control:hover:before, .bg-inverse .btn-outline-primary.active.focus.custom-file-control:before, .bg-inverse .btn-outline-primary.custom-file-control:active.focus:before, .bg-inverse .btn-outline-primary.custom-file-control:active:focus:before, .bg-inverse .btn-outline-primary.custom-file-control:active:hover:before, .bg-inverse .btn.btn-outline-primary.active.focus, .bg-inverse .btn.btn-outline-primary.active:focus, .bg-inverse .btn.btn-outline-primary.active:hover, .bg-inverse .btn.btn-outline-primary:active.focus, .bg-inverse .btn.btn-outline-primary:active:focus, .bg-inverse .btn.btn-outline-primary:active:hover, .open > .bg-inverse .btn-outline-primary.dropdown-toggle.custom-file-control:focus:before, .open > .bg-inverse .btn-outline-primary.dropdown-toggle.custom-file-control:hover:before, .open > .bg-inverse .btn-outline-primary.dropdown-toggle.focus.custom-file-control:before, .open > .bg-inverse .btn.btn-outline-primary.dropdown-toggle.focus, .open > .bg-inverse .btn.btn-outline-primary.dropdown-toggle:focus, .open > .bg-inverse .btn.btn-outline-primary.dropdown-toggle:hover { - color: #009688; - background-color: hsla(0, 0%, 80%, .25); - border-color: hsla(0, 0%, 80%, .25) -} - -.open > .bg-inverse .btn-outline-primary.dropdown-toggle.bmd-btn-icon.custom-file-control:before, .open > .bg-inverse .btn.btn-outline-primary.dropdown-toggle.bmd-btn-icon { - color: inherit; - background-color: transparent -} - -.open > .bg-inverse .btn-outline-primary.dropdown-toggle.bmd-btn-icon.custom-file-control:hover:before, .open > .bg-inverse .btn.btn-outline-primary.dropdown-toggle.bmd-btn-icon:hover { - background-color: hsla(0, 0%, 80%, .15) -} - -.bg-inverse .btn-outline-primary.custom-file-control:disabled.focus:before, .bg-inverse .btn-outline-primary.custom-file-control:disabled:focus:before, .bg-inverse .btn-outline-primary.custom-file-control:disabled:hover:before, .bg-inverse .btn-outline-primary.disabled.custom-file-control:focus:before, .bg-inverse .btn-outline-primary.disabled.custom-file-control:hover:before, .bg-inverse .btn-outline-primary.disabled.focus.custom-file-control:before, .bg-inverse .btn.btn-outline-primary.disabled.focus, .bg-inverse .btn.btn-outline-primary.disabled:focus, .bg-inverse .btn.btn-outline-primary.disabled:hover, .bg-inverse .btn.btn-outline-primary:disabled.focus, .bg-inverse .btn.btn-outline-primary:disabled:focus, .bg-inverse .btn.btn-outline-primary:disabled:hover { - background-color: transparent; - border-color: #009688 -} - -.btn-outline-primary.btn-link.custom-file-control:before, .btn.btn-outline-primary.btn-link { - background-color: transparent -} - -.btn-outline-secondary.custom-file-control:before, .btn.btn-outline-secondary { - border-color: currentColor; - color: #6c757d; - background-color: transparent; - border: 1px solid #6c757d -} - -.btn-outline-secondary.active.custom-file-control:before, .btn-outline-secondary.custom-file-control:active:before, .btn-outline-secondary.custom-file-control:focus:before, .btn-outline-secondary.custom-file-control:hover:before, .btn-outline-secondary.focus.custom-file-control:before, .btn.btn-outline-secondary.active, .btn.btn-outline-secondary.focus, .btn.btn-outline-secondary:active, .btn.btn-outline-secondary:focus, .btn.btn-outline-secondary:hover, .open > .btn-outline-secondary.dropdown-toggle.custom-file-control:before, .open > .btn.btn-outline-secondary.dropdown-toggle { - color: #6c757d; - background-color: hsla(0, 0%, 60%, .2); - border-color: #6c757d -} - -.btn-outline-secondary.active.custom-file-control:focus:before, .btn-outline-secondary.active.custom-file-control:hover:before, .btn-outline-secondary.active.focus.custom-file-control:before, .btn-outline-secondary.custom-file-control:active.focus:before, .btn-outline-secondary.custom-file-control:active:focus:before, .btn-outline-secondary.custom-file-control:active:hover:before, .btn.btn-outline-secondary.active.focus, .btn.btn-outline-secondary.active:focus, .btn.btn-outline-secondary.active:hover, .btn.btn-outline-secondary:active.focus, .btn.btn-outline-secondary:active:focus, .btn.btn-outline-secondary:active:hover, .open > .btn-outline-secondary.dropdown-toggle.custom-file-control:focus:before, .open > .btn-outline-secondary.dropdown-toggle.custom-file-control:hover:before, .open > .btn-outline-secondary.dropdown-toggle.focus.custom-file-control:before, .open > .btn.btn-outline-secondary.dropdown-toggle.focus, .open > .btn.btn-outline-secondary.dropdown-toggle:focus, .open > .btn.btn-outline-secondary.dropdown-toggle:hover { - color: #6c757d; - background-color: hsla(0, 0%, 60%, .4); - border-color: #6c757d -} - -.open > .btn-outline-secondary.dropdown-toggle.bmd-btn-icon.custom-file-control:before, .open > .btn.btn-outline-secondary.dropdown-toggle.bmd-btn-icon { - color: inherit; - background-color: transparent -} - -.open > .btn-outline-secondary.dropdown-toggle.bmd-btn-icon.custom-file-control:hover:before, .open > .btn.btn-outline-secondary.dropdown-toggle.bmd-btn-icon:hover { - background-color: hsla(0, 0%, 60%, .2) -} - -.btn-outline-secondary.custom-file-control:disabled.focus:before, .btn-outline-secondary.custom-file-control:disabled:focus:before, .btn-outline-secondary.custom-file-control:disabled:hover:before, .btn-outline-secondary.disabled.custom-file-control:focus:before, .btn-outline-secondary.disabled.custom-file-control:hover:before, .btn-outline-secondary.disabled.focus.custom-file-control:before, .btn.btn-outline-secondary.disabled.focus, .btn.btn-outline-secondary.disabled:focus, .btn.btn-outline-secondary.disabled:hover, .btn.btn-outline-secondary:disabled.focus, .btn.btn-outline-secondary:disabled:focus, .btn.btn-outline-secondary:disabled:hover { - background-color: transparent; - border-color: #6c757d -} - -.bg-inverse .btn-outline-secondary.custom-file-control:before, .bg-inverse .btn.btn-outline-secondary { - color: #6c757d; - background-color: transparent; - border-color: #6c757d -} - -.bg-inverse .btn-outline-secondary.active.custom-file-control:before, .bg-inverse .btn-outline-secondary.custom-file-control:active:before, .bg-inverse .btn-outline-secondary.custom-file-control:focus:before, .bg-inverse .btn-outline-secondary.custom-file-control:hover:before, .bg-inverse .btn-outline-secondary.focus.custom-file-control:before, .bg-inverse .btn.btn-outline-secondary.active, .bg-inverse .btn.btn-outline-secondary.focus, .bg-inverse .btn.btn-outline-secondary:active, .bg-inverse .btn.btn-outline-secondary:focus, .bg-inverse .btn.btn-outline-secondary:hover, .open > .bg-inverse .btn-outline-secondary.dropdown-toggle.custom-file-control:before, .open > .bg-inverse .btn.btn-outline-secondary.dropdown-toggle { - color: #6c757d; - background-color: hsla(0, 0%, 80%, .15); - border-color: hsla(0, 0%, 80%, .15) -} - -.bg-inverse .btn-outline-secondary.active.custom-file-control:focus:before, .bg-inverse .btn-outline-secondary.active.custom-file-control:hover:before, .bg-inverse .btn-outline-secondary.active.focus.custom-file-control:before, .bg-inverse .btn-outline-secondary.custom-file-control:active.focus:before, .bg-inverse .btn-outline-secondary.custom-file-control:active:focus:before, .bg-inverse .btn-outline-secondary.custom-file-control:active:hover:before, .bg-inverse .btn.btn-outline-secondary.active.focus, .bg-inverse .btn.btn-outline-secondary.active:focus, .bg-inverse .btn.btn-outline-secondary.active:hover, .bg-inverse .btn.btn-outline-secondary:active.focus, .bg-inverse .btn.btn-outline-secondary:active:focus, .bg-inverse .btn.btn-outline-secondary:active:hover, .open > .bg-inverse .btn-outline-secondary.dropdown-toggle.custom-file-control:focus:before, .open > .bg-inverse .btn-outline-secondary.dropdown-toggle.custom-file-control:hover:before, .open > .bg-inverse .btn-outline-secondary.dropdown-toggle.focus.custom-file-control:before, .open > .bg-inverse .btn.btn-outline-secondary.dropdown-toggle.focus, .open > .bg-inverse .btn.btn-outline-secondary.dropdown-toggle:focus, .open > .bg-inverse .btn.btn-outline-secondary.dropdown-toggle:hover { - color: #6c757d; - background-color: hsla(0, 0%, 80%, .25); - border-color: hsla(0, 0%, 80%, .25) -} - -.open > .bg-inverse .btn-outline-secondary.dropdown-toggle.bmd-btn-icon.custom-file-control:before, .open > .bg-inverse .btn.btn-outline-secondary.dropdown-toggle.bmd-btn-icon { - color: inherit; - background-color: transparent -} - -.open > .bg-inverse .btn-outline-secondary.dropdown-toggle.bmd-btn-icon.custom-file-control:hover:before, .open > .bg-inverse .btn.btn-outline-secondary.dropdown-toggle.bmd-btn-icon:hover { - background-color: hsla(0, 0%, 80%, .15) -} - -.bg-inverse .btn-outline-secondary.custom-file-control:disabled.focus:before, .bg-inverse .btn-outline-secondary.custom-file-control:disabled:focus:before, .bg-inverse .btn-outline-secondary.custom-file-control:disabled:hover:before, .bg-inverse .btn-outline-secondary.disabled.custom-file-control:focus:before, .bg-inverse .btn-outline-secondary.disabled.custom-file-control:hover:before, .bg-inverse .btn-outline-secondary.disabled.focus.custom-file-control:before, .bg-inverse .btn.btn-outline-secondary.disabled.focus, .bg-inverse .btn.btn-outline-secondary.disabled:focus, .bg-inverse .btn.btn-outline-secondary.disabled:hover, .bg-inverse .btn.btn-outline-secondary:disabled.focus, .bg-inverse .btn.btn-outline-secondary:disabled:focus, .bg-inverse .btn.btn-outline-secondary:disabled:hover { - background-color: transparent; - border-color: #6c757d -} - -.btn-outline-secondary.btn-link.custom-file-control:before, .btn.btn-outline-secondary.btn-link { - background-color: transparent -} - -.btn-outline-success.custom-file-control:before, .btn.btn-outline-success { - border-color: currentColor; - color: #4caf50; - background-color: transparent; - border: 1px solid #4caf50 -} - -.btn-outline-success.active.custom-file-control:before, .btn-outline-success.custom-file-control:active:before, .btn-outline-success.custom-file-control:focus:before, .btn-outline-success.custom-file-control:hover:before, .btn-outline-success.focus.custom-file-control:before, .btn.btn-outline-success.active, .btn.btn-outline-success.focus, .btn.btn-outline-success:active, .btn.btn-outline-success:focus, .btn.btn-outline-success:hover, .open > .btn-outline-success.dropdown-toggle.custom-file-control:before, .open > .btn.btn-outline-success.dropdown-toggle { - color: #4caf50; - background-color: hsla(0, 0%, 60%, .2); - border-color: #4caf50 -} - -.btn-outline-success.active.custom-file-control:focus:before, .btn-outline-success.active.custom-file-control:hover:before, .btn-outline-success.active.focus.custom-file-control:before, .btn-outline-success.custom-file-control:active.focus:before, .btn-outline-success.custom-file-control:active:focus:before, .btn-outline-success.custom-file-control:active:hover:before, .btn.btn-outline-success.active.focus, .btn.btn-outline-success.active:focus, .btn.btn-outline-success.active:hover, .btn.btn-outline-success:active.focus, .btn.btn-outline-success:active:focus, .btn.btn-outline-success:active:hover, .open > .btn-outline-success.dropdown-toggle.custom-file-control:focus:before, .open > .btn-outline-success.dropdown-toggle.custom-file-control:hover:before, .open > .btn-outline-success.dropdown-toggle.focus.custom-file-control:before, .open > .btn.btn-outline-success.dropdown-toggle.focus, .open > .btn.btn-outline-success.dropdown-toggle:focus, .open > .btn.btn-outline-success.dropdown-toggle:hover { - color: #4caf50; - background-color: hsla(0, 0%, 60%, .4); - border-color: #4caf50 -} - -.open > .btn-outline-success.dropdown-toggle.bmd-btn-icon.custom-file-control:before, .open > .btn.btn-outline-success.dropdown-toggle.bmd-btn-icon { - color: inherit; - background-color: transparent -} - -.open > .btn-outline-success.dropdown-toggle.bmd-btn-icon.custom-file-control:hover:before, .open > .btn.btn-outline-success.dropdown-toggle.bmd-btn-icon:hover { - background-color: hsla(0, 0%, 60%, .2) -} - -.btn-outline-success.custom-file-control:disabled.focus:before, .btn-outline-success.custom-file-control:disabled:focus:before, .btn-outline-success.custom-file-control:disabled:hover:before, .btn-outline-success.disabled.custom-file-control:focus:before, .btn-outline-success.disabled.custom-file-control:hover:before, .btn-outline-success.disabled.focus.custom-file-control:before, .btn.btn-outline-success.disabled.focus, .btn.btn-outline-success.disabled:focus, .btn.btn-outline-success.disabled:hover, .btn.btn-outline-success:disabled.focus, .btn.btn-outline-success:disabled:focus, .btn.btn-outline-success:disabled:hover { - background-color: transparent; - border-color: #4caf50 -} - -.bg-inverse .btn-outline-success.custom-file-control:before, .bg-inverse .btn.btn-outline-success { - color: #4caf50; - background-color: transparent; - border-color: #4caf50 -} - -.bg-inverse .btn-outline-success.active.custom-file-control:before, .bg-inverse .btn-outline-success.custom-file-control:active:before, .bg-inverse .btn-outline-success.custom-file-control:focus:before, .bg-inverse .btn-outline-success.custom-file-control:hover:before, .bg-inverse .btn-outline-success.focus.custom-file-control:before, .bg-inverse .btn.btn-outline-success.active, .bg-inverse .btn.btn-outline-success.focus, .bg-inverse .btn.btn-outline-success:active, .bg-inverse .btn.btn-outline-success:focus, .bg-inverse .btn.btn-outline-success:hover, .open > .bg-inverse .btn-outline-success.dropdown-toggle.custom-file-control:before, .open > .bg-inverse .btn.btn-outline-success.dropdown-toggle { - color: #4caf50; - background-color: hsla(0, 0%, 80%, .15); - border-color: hsla(0, 0%, 80%, .15) -} - -.bg-inverse .btn-outline-success.active.custom-file-control:focus:before, .bg-inverse .btn-outline-success.active.custom-file-control:hover:before, .bg-inverse .btn-outline-success.active.focus.custom-file-control:before, .bg-inverse .btn-outline-success.custom-file-control:active.focus:before, .bg-inverse .btn-outline-success.custom-file-control:active:focus:before, .bg-inverse .btn-outline-success.custom-file-control:active:hover:before, .bg-inverse .btn.btn-outline-success.active.focus, .bg-inverse .btn.btn-outline-success.active:focus, .bg-inverse .btn.btn-outline-success.active:hover, .bg-inverse .btn.btn-outline-success:active.focus, .bg-inverse .btn.btn-outline-success:active:focus, .bg-inverse .btn.btn-outline-success:active:hover, .open > .bg-inverse .btn-outline-success.dropdown-toggle.custom-file-control:focus:before, .open > .bg-inverse .btn-outline-success.dropdown-toggle.custom-file-control:hover:before, .open > .bg-inverse .btn-outline-success.dropdown-toggle.focus.custom-file-control:before, .open > .bg-inverse .btn.btn-outline-success.dropdown-toggle.focus, .open > .bg-inverse .btn.btn-outline-success.dropdown-toggle:focus, .open > .bg-inverse .btn.btn-outline-success.dropdown-toggle:hover { - color: #4caf50; - background-color: hsla(0, 0%, 80%, .25); - border-color: hsla(0, 0%, 80%, .25) -} - -.open > .bg-inverse .btn-outline-success.dropdown-toggle.bmd-btn-icon.custom-file-control:before, .open > .bg-inverse .btn.btn-outline-success.dropdown-toggle.bmd-btn-icon { - color: inherit; - background-color: transparent -} - -.open > .bg-inverse .btn-outline-success.dropdown-toggle.bmd-btn-icon.custom-file-control:hover:before, .open > .bg-inverse .btn.btn-outline-success.dropdown-toggle.bmd-btn-icon:hover { - background-color: hsla(0, 0%, 80%, .15) -} - -.bg-inverse .btn-outline-success.custom-file-control:disabled.focus:before, .bg-inverse .btn-outline-success.custom-file-control:disabled:focus:before, .bg-inverse .btn-outline-success.custom-file-control:disabled:hover:before, .bg-inverse .btn-outline-success.disabled.custom-file-control:focus:before, .bg-inverse .btn-outline-success.disabled.custom-file-control:hover:before, .bg-inverse .btn-outline-success.disabled.focus.custom-file-control:before, .bg-inverse .btn.btn-outline-success.disabled.focus, .bg-inverse .btn.btn-outline-success.disabled:focus, .bg-inverse .btn.btn-outline-success.disabled:hover, .bg-inverse .btn.btn-outline-success:disabled.focus, .bg-inverse .btn.btn-outline-success:disabled:focus, .bg-inverse .btn.btn-outline-success:disabled:hover { - background-color: transparent; - border-color: #4caf50 -} - -.btn-outline-success.btn-link.custom-file-control:before, .btn.btn-outline-success.btn-link { - background-color: transparent -} - -.btn-outline-info.custom-file-control:before, .btn.btn-outline-info { - border-color: currentColor; - color: #03a9f4; - background-color: transparent; - border: 1px solid #03a9f4 -} - -.btn-outline-info.active.custom-file-control:before, .btn-outline-info.custom-file-control:active:before, .btn-outline-info.custom-file-control:focus:before, .btn-outline-info.custom-file-control:hover:before, .btn-outline-info.focus.custom-file-control:before, .btn.btn-outline-info.active, .btn.btn-outline-info.focus, .btn.btn-outline-info:active, .btn.btn-outline-info:focus, .btn.btn-outline-info:hover, .open > .btn-outline-info.dropdown-toggle.custom-file-control:before, .open > .btn.btn-outline-info.dropdown-toggle { - color: #03a9f4; - background-color: hsla(0, 0%, 60%, .2); - border-color: #03a9f4 -} - -.btn-outline-info.active.custom-file-control:focus:before, .btn-outline-info.active.custom-file-control:hover:before, .btn-outline-info.active.focus.custom-file-control:before, .btn-outline-info.custom-file-control:active.focus:before, .btn-outline-info.custom-file-control:active:focus:before, .btn-outline-info.custom-file-control:active:hover:before, .btn.btn-outline-info.active.focus, .btn.btn-outline-info.active:focus, .btn.btn-outline-info.active:hover, .btn.btn-outline-info:active.focus, .btn.btn-outline-info:active:focus, .btn.btn-outline-info:active:hover, .open > .btn-outline-info.dropdown-toggle.custom-file-control:focus:before, .open > .btn-outline-info.dropdown-toggle.custom-file-control:hover:before, .open > .btn-outline-info.dropdown-toggle.focus.custom-file-control:before, .open > .btn.btn-outline-info.dropdown-toggle.focus, .open > .btn.btn-outline-info.dropdown-toggle:focus, .open > .btn.btn-outline-info.dropdown-toggle:hover { - color: #03a9f4; - background-color: hsla(0, 0%, 60%, .4); - border-color: #03a9f4 -} - -.open > .btn-outline-info.dropdown-toggle.bmd-btn-icon.custom-file-control:before, .open > .btn.btn-outline-info.dropdown-toggle.bmd-btn-icon { - color: inherit; - background-color: transparent -} - -.open > .btn-outline-info.dropdown-toggle.bmd-btn-icon.custom-file-control:hover:before, .open > .btn.btn-outline-info.dropdown-toggle.bmd-btn-icon:hover { - background-color: hsla(0, 0%, 60%, .2) -} - -.btn-outline-info.custom-file-control:disabled.focus:before, .btn-outline-info.custom-file-control:disabled:focus:before, .btn-outline-info.custom-file-control:disabled:hover:before, .btn-outline-info.disabled.custom-file-control:focus:before, .btn-outline-info.disabled.custom-file-control:hover:before, .btn-outline-info.disabled.focus.custom-file-control:before, .btn.btn-outline-info.disabled.focus, .btn.btn-outline-info.disabled:focus, .btn.btn-outline-info.disabled:hover, .btn.btn-outline-info:disabled.focus, .btn.btn-outline-info:disabled:focus, .btn.btn-outline-info:disabled:hover { - background-color: transparent; - border-color: #03a9f4 -} - -.bg-inverse .btn-outline-info.custom-file-control:before, .bg-inverse .btn.btn-outline-info { - color: #03a9f4; - background-color: transparent; - border-color: #03a9f4 -} - -.bg-inverse .btn-outline-info.active.custom-file-control:before, .bg-inverse .btn-outline-info.custom-file-control:active:before, .bg-inverse .btn-outline-info.custom-file-control:focus:before, .bg-inverse .btn-outline-info.custom-file-control:hover:before, .bg-inverse .btn-outline-info.focus.custom-file-control:before, .bg-inverse .btn.btn-outline-info.active, .bg-inverse .btn.btn-outline-info.focus, .bg-inverse .btn.btn-outline-info:active, .bg-inverse .btn.btn-outline-info:focus, .bg-inverse .btn.btn-outline-info:hover, .open > .bg-inverse .btn-outline-info.dropdown-toggle.custom-file-control:before, .open > .bg-inverse .btn.btn-outline-info.dropdown-toggle { - color: #03a9f4; - background-color: hsla(0, 0%, 80%, .15); - border-color: hsla(0, 0%, 80%, .15) -} - -.bg-inverse .btn-outline-info.active.custom-file-control:focus:before, .bg-inverse .btn-outline-info.active.custom-file-control:hover:before, .bg-inverse .btn-outline-info.active.focus.custom-file-control:before, .bg-inverse .btn-outline-info.custom-file-control:active.focus:before, .bg-inverse .btn-outline-info.custom-file-control:active:focus:before, .bg-inverse .btn-outline-info.custom-file-control:active:hover:before, .bg-inverse .btn.btn-outline-info.active.focus, .bg-inverse .btn.btn-outline-info.active:focus, .bg-inverse .btn.btn-outline-info.active:hover, .bg-inverse .btn.btn-outline-info:active.focus, .bg-inverse .btn.btn-outline-info:active:focus, .bg-inverse .btn.btn-outline-info:active:hover, .open > .bg-inverse .btn-outline-info.dropdown-toggle.custom-file-control:focus:before, .open > .bg-inverse .btn-outline-info.dropdown-toggle.custom-file-control:hover:before, .open > .bg-inverse .btn-outline-info.dropdown-toggle.focus.custom-file-control:before, .open > .bg-inverse .btn.btn-outline-info.dropdown-toggle.focus, .open > .bg-inverse .btn.btn-outline-info.dropdown-toggle:focus, .open > .bg-inverse .btn.btn-outline-info.dropdown-toggle:hover { - color: #03a9f4; - background-color: hsla(0, 0%, 80%, .25); - border-color: hsla(0, 0%, 80%, .25) -} - -.open > .bg-inverse .btn-outline-info.dropdown-toggle.bmd-btn-icon.custom-file-control:before, .open > .bg-inverse .btn.btn-outline-info.dropdown-toggle.bmd-btn-icon { - color: inherit; - background-color: transparent -} - -.open > .bg-inverse .btn-outline-info.dropdown-toggle.bmd-btn-icon.custom-file-control:hover:before, .open > .bg-inverse .btn.btn-outline-info.dropdown-toggle.bmd-btn-icon:hover { - background-color: hsla(0, 0%, 80%, .15) -} - -.bg-inverse .btn-outline-info.custom-file-control:disabled.focus:before, .bg-inverse .btn-outline-info.custom-file-control:disabled:focus:before, .bg-inverse .btn-outline-info.custom-file-control:disabled:hover:before, .bg-inverse .btn-outline-info.disabled.custom-file-control:focus:before, .bg-inverse .btn-outline-info.disabled.custom-file-control:hover:before, .bg-inverse .btn-outline-info.disabled.focus.custom-file-control:before, .bg-inverse .btn.btn-outline-info.disabled.focus, .bg-inverse .btn.btn-outline-info.disabled:focus, .bg-inverse .btn.btn-outline-info.disabled:hover, .bg-inverse .btn.btn-outline-info:disabled.focus, .bg-inverse .btn.btn-outline-info:disabled:focus, .bg-inverse .btn.btn-outline-info:disabled:hover { - background-color: transparent; - border-color: #03a9f4 -} - -.btn-outline-info.btn-link.custom-file-control:before, .btn.btn-outline-info.btn-link { - background-color: transparent -} - -.btn-outline-warning.custom-file-control:before, .btn.btn-outline-warning { - border-color: currentColor; - color: #ff5722; - background-color: transparent; - border: 1px solid #ff5722 -} - -.btn-outline-warning.active.custom-file-control:before, .btn-outline-warning.custom-file-control:active:before, .btn-outline-warning.custom-file-control:focus:before, .btn-outline-warning.custom-file-control:hover:before, .btn-outline-warning.focus.custom-file-control:before, .btn.btn-outline-warning.active, .btn.btn-outline-warning.focus, .btn.btn-outline-warning:active, .btn.btn-outline-warning:focus, .btn.btn-outline-warning:hover, .open > .btn-outline-warning.dropdown-toggle.custom-file-control:before, .open > .btn.btn-outline-warning.dropdown-toggle { - color: #ff5722; - background-color: hsla(0, 0%, 60%, .2); - border-color: #ff5722 -} - -.btn-outline-warning.active.custom-file-control:focus:before, .btn-outline-warning.active.custom-file-control:hover:before, .btn-outline-warning.active.focus.custom-file-control:before, .btn-outline-warning.custom-file-control:active.focus:before, .btn-outline-warning.custom-file-control:active:focus:before, .btn-outline-warning.custom-file-control:active:hover:before, .btn.btn-outline-warning.active.focus, .btn.btn-outline-warning.active:focus, .btn.btn-outline-warning.active:hover, .btn.btn-outline-warning:active.focus, .btn.btn-outline-warning:active:focus, .btn.btn-outline-warning:active:hover, .open > .btn-outline-warning.dropdown-toggle.custom-file-control:focus:before, .open > .btn-outline-warning.dropdown-toggle.custom-file-control:hover:before, .open > .btn-outline-warning.dropdown-toggle.focus.custom-file-control:before, .open > .btn.btn-outline-warning.dropdown-toggle.focus, .open > .btn.btn-outline-warning.dropdown-toggle:focus, .open > .btn.btn-outline-warning.dropdown-toggle:hover { - color: #ff5722; - background-color: hsla(0, 0%, 60%, .4); - border-color: #ff5722 -} - -.open > .btn-outline-warning.dropdown-toggle.bmd-btn-icon.custom-file-control:before, .open > .btn.btn-outline-warning.dropdown-toggle.bmd-btn-icon { - color: inherit; - background-color: transparent -} - -.open > .btn-outline-warning.dropdown-toggle.bmd-btn-icon.custom-file-control:hover:before, .open > .btn.btn-outline-warning.dropdown-toggle.bmd-btn-icon:hover { - background-color: hsla(0, 0%, 60%, .2) -} - -.btn-outline-warning.custom-file-control:disabled.focus:before, .btn-outline-warning.custom-file-control:disabled:focus:before, .btn-outline-warning.custom-file-control:disabled:hover:before, .btn-outline-warning.disabled.custom-file-control:focus:before, .btn-outline-warning.disabled.custom-file-control:hover:before, .btn-outline-warning.disabled.focus.custom-file-control:before, .btn.btn-outline-warning.disabled.focus, .btn.btn-outline-warning.disabled:focus, .btn.btn-outline-warning.disabled:hover, .btn.btn-outline-warning:disabled.focus, .btn.btn-outline-warning:disabled:focus, .btn.btn-outline-warning:disabled:hover { - background-color: transparent; - border-color: #ff5722 -} - -.bg-inverse .btn-outline-warning.custom-file-control:before, .bg-inverse .btn.btn-outline-warning { - color: #ff5722; - background-color: transparent; - border-color: #ff5722 -} - -.bg-inverse .btn-outline-warning.active.custom-file-control:before, .bg-inverse .btn-outline-warning.custom-file-control:active:before, .bg-inverse .btn-outline-warning.custom-file-control:focus:before, .bg-inverse .btn-outline-warning.custom-file-control:hover:before, .bg-inverse .btn-outline-warning.focus.custom-file-control:before, .bg-inverse .btn.btn-outline-warning.active, .bg-inverse .btn.btn-outline-warning.focus, .bg-inverse .btn.btn-outline-warning:active, .bg-inverse .btn.btn-outline-warning:focus, .bg-inverse .btn.btn-outline-warning:hover, .open > .bg-inverse .btn-outline-warning.dropdown-toggle.custom-file-control:before, .open > .bg-inverse .btn.btn-outline-warning.dropdown-toggle { - color: #ff5722; - background-color: hsla(0, 0%, 80%, .15); - border-color: hsla(0, 0%, 80%, .15) -} - -.bg-inverse .btn-outline-warning.active.custom-file-control:focus:before, .bg-inverse .btn-outline-warning.active.custom-file-control:hover:before, .bg-inverse .btn-outline-warning.active.focus.custom-file-control:before, .bg-inverse .btn-outline-warning.custom-file-control:active.focus:before, .bg-inverse .btn-outline-warning.custom-file-control:active:focus:before, .bg-inverse .btn-outline-warning.custom-file-control:active:hover:before, .bg-inverse .btn.btn-outline-warning.active.focus, .bg-inverse .btn.btn-outline-warning.active:focus, .bg-inverse .btn.btn-outline-warning.active:hover, .bg-inverse .btn.btn-outline-warning:active.focus, .bg-inverse .btn.btn-outline-warning:active:focus, .bg-inverse .btn.btn-outline-warning:active:hover, .open > .bg-inverse .btn-outline-warning.dropdown-toggle.custom-file-control:focus:before, .open > .bg-inverse .btn-outline-warning.dropdown-toggle.custom-file-control:hover:before, .open > .bg-inverse .btn-outline-warning.dropdown-toggle.focus.custom-file-control:before, .open > .bg-inverse .btn.btn-outline-warning.dropdown-toggle.focus, .open > .bg-inverse .btn.btn-outline-warning.dropdown-toggle:focus, .open > .bg-inverse .btn.btn-outline-warning.dropdown-toggle:hover { - color: #ff5722; - background-color: hsla(0, 0%, 80%, .25); - border-color: hsla(0, 0%, 80%, .25) -} - -.open > .bg-inverse .btn-outline-warning.dropdown-toggle.bmd-btn-icon.custom-file-control:before, .open > .bg-inverse .btn.btn-outline-warning.dropdown-toggle.bmd-btn-icon { - color: inherit; - background-color: transparent -} - -.open > .bg-inverse .btn-outline-warning.dropdown-toggle.bmd-btn-icon.custom-file-control:hover:before, .open > .bg-inverse .btn.btn-outline-warning.dropdown-toggle.bmd-btn-icon:hover { - background-color: hsla(0, 0%, 80%, .15) -} - -.bg-inverse .btn-outline-warning.custom-file-control:disabled.focus:before, .bg-inverse .btn-outline-warning.custom-file-control:disabled:focus:before, .bg-inverse .btn-outline-warning.custom-file-control:disabled:hover:before, .bg-inverse .btn-outline-warning.disabled.custom-file-control:focus:before, .bg-inverse .btn-outline-warning.disabled.custom-file-control:hover:before, .bg-inverse .btn-outline-warning.disabled.focus.custom-file-control:before, .bg-inverse .btn.btn-outline-warning.disabled.focus, .bg-inverse .btn.btn-outline-warning.disabled:focus, .bg-inverse .btn.btn-outline-warning.disabled:hover, .bg-inverse .btn.btn-outline-warning:disabled.focus, .bg-inverse .btn.btn-outline-warning:disabled:focus, .bg-inverse .btn.btn-outline-warning:disabled:hover { - background-color: transparent; - border-color: #ff5722 -} - -.btn-outline-warning.btn-link.custom-file-control:before, .btn.btn-outline-warning.btn-link { - background-color: transparent -} - -.btn-outline-danger.custom-file-control:before, .btn.btn-outline-danger { - border-color: currentColor; - color: #f44336; - background-color: transparent; - border: 1px solid #f44336 -} - -.btn-outline-danger.active.custom-file-control:before, .btn-outline-danger.custom-file-control:active:before, .btn-outline-danger.custom-file-control:focus:before, .btn-outline-danger.custom-file-control:hover:before, .btn-outline-danger.focus.custom-file-control:before, .btn.btn-outline-danger.active, .btn.btn-outline-danger.focus, .btn.btn-outline-danger:active, .btn.btn-outline-danger:focus, .btn.btn-outline-danger:hover, .open > .btn-outline-danger.dropdown-toggle.custom-file-control:before, .open > .btn.btn-outline-danger.dropdown-toggle { - color: #f44336; - background-color: hsla(0, 0%, 60%, .2); - border-color: #f44336 -} - -.btn-outline-danger.active.custom-file-control:focus:before, .btn-outline-danger.active.custom-file-control:hover:before, .btn-outline-danger.active.focus.custom-file-control:before, .btn-outline-danger.custom-file-control:active.focus:before, .btn-outline-danger.custom-file-control:active:focus:before, .btn-outline-danger.custom-file-control:active:hover:before, .btn.btn-outline-danger.active.focus, .btn.btn-outline-danger.active:focus, .btn.btn-outline-danger.active:hover, .btn.btn-outline-danger:active.focus, .btn.btn-outline-danger:active:focus, .btn.btn-outline-danger:active:hover, .open > .btn-outline-danger.dropdown-toggle.custom-file-control:focus:before, .open > .btn-outline-danger.dropdown-toggle.custom-file-control:hover:before, .open > .btn-outline-danger.dropdown-toggle.focus.custom-file-control:before, .open > .btn.btn-outline-danger.dropdown-toggle.focus, .open > .btn.btn-outline-danger.dropdown-toggle:focus, .open > .btn.btn-outline-danger.dropdown-toggle:hover { - color: #f44336; - background-color: hsla(0, 0%, 60%, .4); - border-color: #f44336 -} - -.open > .btn-outline-danger.dropdown-toggle.bmd-btn-icon.custom-file-control:before, .open > .btn.btn-outline-danger.dropdown-toggle.bmd-btn-icon { - color: inherit; - background-color: transparent -} - -.open > .btn-outline-danger.dropdown-toggle.bmd-btn-icon.custom-file-control:hover:before, .open > .btn.btn-outline-danger.dropdown-toggle.bmd-btn-icon:hover { - background-color: hsla(0, 0%, 60%, .2) -} - -.btn-outline-danger.custom-file-control:disabled.focus:before, .btn-outline-danger.custom-file-control:disabled:focus:before, .btn-outline-danger.custom-file-control:disabled:hover:before, .btn-outline-danger.disabled.custom-file-control:focus:before, .btn-outline-danger.disabled.custom-file-control:hover:before, .btn-outline-danger.disabled.focus.custom-file-control:before, .btn.btn-outline-danger.disabled.focus, .btn.btn-outline-danger.disabled:focus, .btn.btn-outline-danger.disabled:hover, .btn.btn-outline-danger:disabled.focus, .btn.btn-outline-danger:disabled:focus, .btn.btn-outline-danger:disabled:hover { - background-color: transparent; - border-color: #f44336 -} - -.bg-inverse .btn-outline-danger.custom-file-control:before, .bg-inverse .btn.btn-outline-danger { - color: #f44336; - background-color: transparent; - border-color: #f44336 -} - -.bg-inverse .btn-outline-danger.active.custom-file-control:before, .bg-inverse .btn-outline-danger.custom-file-control:active:before, .bg-inverse .btn-outline-danger.custom-file-control:focus:before, .bg-inverse .btn-outline-danger.custom-file-control:hover:before, .bg-inverse .btn-outline-danger.focus.custom-file-control:before, .bg-inverse .btn.btn-outline-danger.active, .bg-inverse .btn.btn-outline-danger.focus, .bg-inverse .btn.btn-outline-danger:active, .bg-inverse .btn.btn-outline-danger:focus, .bg-inverse .btn.btn-outline-danger:hover, .open > .bg-inverse .btn-outline-danger.dropdown-toggle.custom-file-control:before, .open > .bg-inverse .btn.btn-outline-danger.dropdown-toggle { - color: #f44336; - background-color: hsla(0, 0%, 80%, .15); - border-color: hsla(0, 0%, 80%, .15) -} - -.bg-inverse .btn-outline-danger.active.custom-file-control:focus:before, .bg-inverse .btn-outline-danger.active.custom-file-control:hover:before, .bg-inverse .btn-outline-danger.active.focus.custom-file-control:before, .bg-inverse .btn-outline-danger.custom-file-control:active.focus:before, .bg-inverse .btn-outline-danger.custom-file-control:active:focus:before, .bg-inverse .btn-outline-danger.custom-file-control:active:hover:before, .bg-inverse .btn.btn-outline-danger.active.focus, .bg-inverse .btn.btn-outline-danger.active:focus, .bg-inverse .btn.btn-outline-danger.active:hover, .bg-inverse .btn.btn-outline-danger:active.focus, .bg-inverse .btn.btn-outline-danger:active:focus, .bg-inverse .btn.btn-outline-danger:active:hover, .open > .bg-inverse .btn-outline-danger.dropdown-toggle.custom-file-control:focus:before, .open > .bg-inverse .btn-outline-danger.dropdown-toggle.custom-file-control:hover:before, .open > .bg-inverse .btn-outline-danger.dropdown-toggle.focus.custom-file-control:before, .open > .bg-inverse .btn.btn-outline-danger.dropdown-toggle.focus, .open > .bg-inverse .btn.btn-outline-danger.dropdown-toggle:focus, .open > .bg-inverse .btn.btn-outline-danger.dropdown-toggle:hover { - color: #f44336; - background-color: hsla(0, 0%, 80%, .25); - border-color: hsla(0, 0%, 80%, .25) -} - -.open > .bg-inverse .btn-outline-danger.dropdown-toggle.bmd-btn-icon.custom-file-control:before, .open > .bg-inverse .btn.btn-outline-danger.dropdown-toggle.bmd-btn-icon { - color: inherit; - background-color: transparent -} - -.open > .bg-inverse .btn-outline-danger.dropdown-toggle.bmd-btn-icon.custom-file-control:hover:before, .open > .bg-inverse .btn.btn-outline-danger.dropdown-toggle.bmd-btn-icon:hover { - background-color: hsla(0, 0%, 80%, .15) -} - -.bg-inverse .btn-outline-danger.custom-file-control:disabled.focus:before, .bg-inverse .btn-outline-danger.custom-file-control:disabled:focus:before, .bg-inverse .btn-outline-danger.custom-file-control:disabled:hover:before, .bg-inverse .btn-outline-danger.disabled.custom-file-control:focus:before, .bg-inverse .btn-outline-danger.disabled.custom-file-control:hover:before, .bg-inverse .btn-outline-danger.disabled.focus.custom-file-control:before, .bg-inverse .btn.btn-outline-danger.disabled.focus, .bg-inverse .btn.btn-outline-danger.disabled:focus, .bg-inverse .btn.btn-outline-danger.disabled:hover, .bg-inverse .btn.btn-outline-danger:disabled.focus, .bg-inverse .btn.btn-outline-danger:disabled:focus, .bg-inverse .btn.btn-outline-danger:disabled:hover { - background-color: transparent; - border-color: #f44336 -} - -.btn-outline-danger.btn-link.custom-file-control:before, .btn.btn-outline-danger.btn-link { - background-color: transparent -} - -.btn-outline-light.custom-file-control:before, .btn.btn-outline-light { - border-color: currentColor; - color: #f5f5f5; - background-color: transparent; - border: 1px solid #f5f5f5 -} - -.btn-outline-light.active.custom-file-control:before, .btn-outline-light.custom-file-control:active:before, .btn-outline-light.custom-file-control:focus:before, .btn-outline-light.custom-file-control:hover:before, .btn-outline-light.focus.custom-file-control:before, .btn.btn-outline-light.active, .btn.btn-outline-light.focus, .btn.btn-outline-light:active, .btn.btn-outline-light:focus, .btn.btn-outline-light:hover, .open > .btn-outline-light.dropdown-toggle.custom-file-control:before, .open > .btn.btn-outline-light.dropdown-toggle { - color: #f5f5f5; - background-color: hsla(0, 0%, 60%, .2); - border-color: #f5f5f5 -} - -.btn-outline-light.active.custom-file-control:focus:before, .btn-outline-light.active.custom-file-control:hover:before, .btn-outline-light.active.focus.custom-file-control:before, .btn-outline-light.custom-file-control:active.focus:before, .btn-outline-light.custom-file-control:active:focus:before, .btn-outline-light.custom-file-control:active:hover:before, .btn.btn-outline-light.active.focus, .btn.btn-outline-light.active:focus, .btn.btn-outline-light.active:hover, .btn.btn-outline-light:active.focus, .btn.btn-outline-light:active:focus, .btn.btn-outline-light:active:hover, .open > .btn-outline-light.dropdown-toggle.custom-file-control:focus:before, .open > .btn-outline-light.dropdown-toggle.custom-file-control:hover:before, .open > .btn-outline-light.dropdown-toggle.focus.custom-file-control:before, .open > .btn.btn-outline-light.dropdown-toggle.focus, .open > .btn.btn-outline-light.dropdown-toggle:focus, .open > .btn.btn-outline-light.dropdown-toggle:hover { - color: #f5f5f5; - background-color: hsla(0, 0%, 60%, .4); - border-color: #f5f5f5 -} - -.open > .btn-outline-light.dropdown-toggle.bmd-btn-icon.custom-file-control:before, .open > .btn.btn-outline-light.dropdown-toggle.bmd-btn-icon { - color: inherit; - background-color: transparent -} - -.open > .btn-outline-light.dropdown-toggle.bmd-btn-icon.custom-file-control:hover:before, .open > .btn.btn-outline-light.dropdown-toggle.bmd-btn-icon:hover { - background-color: hsla(0, 0%, 60%, .2) -} - -.btn-outline-light.custom-file-control:disabled.focus:before, .btn-outline-light.custom-file-control:disabled:focus:before, .btn-outline-light.custom-file-control:disabled:hover:before, .btn-outline-light.disabled.custom-file-control:focus:before, .btn-outline-light.disabled.custom-file-control:hover:before, .btn-outline-light.disabled.focus.custom-file-control:before, .btn.btn-outline-light.disabled.focus, .btn.btn-outline-light.disabled:focus, .btn.btn-outline-light.disabled:hover, .btn.btn-outline-light:disabled.focus, .btn.btn-outline-light:disabled:focus, .btn.btn-outline-light:disabled:hover { - background-color: transparent; - border-color: #f5f5f5 -} - -.bg-inverse .btn-outline-light.custom-file-control:before, .bg-inverse .btn.btn-outline-light { - color: #f5f5f5; - background-color: transparent; - border-color: #f5f5f5 -} - -.bg-inverse .btn-outline-light.active.custom-file-control:before, .bg-inverse .btn-outline-light.custom-file-control:active:before, .bg-inverse .btn-outline-light.custom-file-control:focus:before, .bg-inverse .btn-outline-light.custom-file-control:hover:before, .bg-inverse .btn-outline-light.focus.custom-file-control:before, .bg-inverse .btn.btn-outline-light.active, .bg-inverse .btn.btn-outline-light.focus, .bg-inverse .btn.btn-outline-light:active, .bg-inverse .btn.btn-outline-light:focus, .bg-inverse .btn.btn-outline-light:hover, .open > .bg-inverse .btn-outline-light.dropdown-toggle.custom-file-control:before, .open > .bg-inverse .btn.btn-outline-light.dropdown-toggle { - color: #f5f5f5; - background-color: hsla(0, 0%, 80%, .15); - border-color: hsla(0, 0%, 80%, .15) -} - -.bg-inverse .btn-outline-light.active.custom-file-control:focus:before, .bg-inverse .btn-outline-light.active.custom-file-control:hover:before, .bg-inverse .btn-outline-light.active.focus.custom-file-control:before, .bg-inverse .btn-outline-light.custom-file-control:active.focus:before, .bg-inverse .btn-outline-light.custom-file-control:active:focus:before, .bg-inverse .btn-outline-light.custom-file-control:active:hover:before, .bg-inverse .btn.btn-outline-light.active.focus, .bg-inverse .btn.btn-outline-light.active:focus, .bg-inverse .btn.btn-outline-light.active:hover, .bg-inverse .btn.btn-outline-light:active.focus, .bg-inverse .btn.btn-outline-light:active:focus, .bg-inverse .btn.btn-outline-light:active:hover, .open > .bg-inverse .btn-outline-light.dropdown-toggle.custom-file-control:focus:before, .open > .bg-inverse .btn-outline-light.dropdown-toggle.custom-file-control:hover:before, .open > .bg-inverse .btn-outline-light.dropdown-toggle.focus.custom-file-control:before, .open > .bg-inverse .btn.btn-outline-light.dropdown-toggle.focus, .open > .bg-inverse .btn.btn-outline-light.dropdown-toggle:focus, .open > .bg-inverse .btn.btn-outline-light.dropdown-toggle:hover { - color: #f5f5f5; - background-color: hsla(0, 0%, 80%, .25); - border-color: hsla(0, 0%, 80%, .25) -} - -.open > .bg-inverse .btn-outline-light.dropdown-toggle.bmd-btn-icon.custom-file-control:before, .open > .bg-inverse .btn.btn-outline-light.dropdown-toggle.bmd-btn-icon { - color: inherit; - background-color: transparent -} - -.open > .bg-inverse .btn-outline-light.dropdown-toggle.bmd-btn-icon.custom-file-control:hover:before, .open > .bg-inverse .btn.btn-outline-light.dropdown-toggle.bmd-btn-icon:hover { - background-color: hsla(0, 0%, 80%, .15) -} - -.bg-inverse .btn-outline-light.custom-file-control:disabled.focus:before, .bg-inverse .btn-outline-light.custom-file-control:disabled:focus:before, .bg-inverse .btn-outline-light.custom-file-control:disabled:hover:before, .bg-inverse .btn-outline-light.disabled.custom-file-control:focus:before, .bg-inverse .btn-outline-light.disabled.custom-file-control:hover:before, .bg-inverse .btn-outline-light.disabled.focus.custom-file-control:before, .bg-inverse .btn.btn-outline-light.disabled.focus, .bg-inverse .btn.btn-outline-light.disabled:focus, .bg-inverse .btn.btn-outline-light.disabled:hover, .bg-inverse .btn.btn-outline-light:disabled.focus, .bg-inverse .btn.btn-outline-light:disabled:focus, .bg-inverse .btn.btn-outline-light:disabled:hover { - background-color: transparent; - border-color: #f5f5f5 -} - -.btn-outline-light.btn-link.custom-file-control:before, .btn.btn-outline-light.btn-link { - background-color: transparent -} - -.btn-outline-dark.custom-file-control:before, .btn.btn-outline-dark { - border-color: currentColor; - color: #424242; - background-color: transparent; - border: 1px solid #424242 -} - -.btn-outline-dark.active.custom-file-control:before, .btn-outline-dark.custom-file-control:active:before, .btn-outline-dark.custom-file-control:focus:before, .btn-outline-dark.custom-file-control:hover:before, .btn-outline-dark.focus.custom-file-control:before, .btn.btn-outline-dark.active, .btn.btn-outline-dark.focus, .btn.btn-outline-dark:active, .btn.btn-outline-dark:focus, .btn.btn-outline-dark:hover, .open > .btn-outline-dark.dropdown-toggle.custom-file-control:before, .open > .btn.btn-outline-dark.dropdown-toggle { - color: #424242; - background-color: hsla(0, 0%, 60%, .2); - border-color: #424242 -} - -.btn-outline-dark.active.custom-file-control:focus:before, .btn-outline-dark.active.custom-file-control:hover:before, .btn-outline-dark.active.focus.custom-file-control:before, .btn-outline-dark.custom-file-control:active.focus:before, .btn-outline-dark.custom-file-control:active:focus:before, .btn-outline-dark.custom-file-control:active:hover:before, .btn.btn-outline-dark.active.focus, .btn.btn-outline-dark.active:focus, .btn.btn-outline-dark.active:hover, .btn.btn-outline-dark:active.focus, .btn.btn-outline-dark:active:focus, .btn.btn-outline-dark:active:hover, .open > .btn-outline-dark.dropdown-toggle.custom-file-control:focus:before, .open > .btn-outline-dark.dropdown-toggle.custom-file-control:hover:before, .open > .btn-outline-dark.dropdown-toggle.focus.custom-file-control:before, .open > .btn.btn-outline-dark.dropdown-toggle.focus, .open > .btn.btn-outline-dark.dropdown-toggle:focus, .open > .btn.btn-outline-dark.dropdown-toggle:hover { - color: #424242; - background-color: hsla(0, 0%, 60%, .4); - border-color: #424242 -} - -.open > .btn-outline-dark.dropdown-toggle.bmd-btn-icon.custom-file-control:before, .open > .btn.btn-outline-dark.dropdown-toggle.bmd-btn-icon { - color: inherit; - background-color: transparent -} - -.open > .btn-outline-dark.dropdown-toggle.bmd-btn-icon.custom-file-control:hover:before, .open > .btn.btn-outline-dark.dropdown-toggle.bmd-btn-icon:hover { - background-color: hsla(0, 0%, 60%, .2) -} - -.btn-outline-dark.custom-file-control:disabled.focus:before, .btn-outline-dark.custom-file-control:disabled:focus:before, .btn-outline-dark.custom-file-control:disabled:hover:before, .btn-outline-dark.disabled.custom-file-control:focus:before, .btn-outline-dark.disabled.custom-file-control:hover:before, .btn-outline-dark.disabled.focus.custom-file-control:before, .btn.btn-outline-dark.disabled.focus, .btn.btn-outline-dark.disabled:focus, .btn.btn-outline-dark.disabled:hover, .btn.btn-outline-dark:disabled.focus, .btn.btn-outline-dark:disabled:focus, .btn.btn-outline-dark:disabled:hover { - background-color: transparent; - border-color: #424242 -} - -.bg-inverse .btn-outline-dark.custom-file-control:before, .bg-inverse .btn.btn-outline-dark { - color: #424242; - background-color: transparent; - border-color: #424242 -} - -.bg-inverse .btn-outline-dark.active.custom-file-control:before, .bg-inverse .btn-outline-dark.custom-file-control:active:before, .bg-inverse .btn-outline-dark.custom-file-control:focus:before, .bg-inverse .btn-outline-dark.custom-file-control:hover:before, .bg-inverse .btn-outline-dark.focus.custom-file-control:before, .bg-inverse .btn.btn-outline-dark.active, .bg-inverse .btn.btn-outline-dark.focus, .bg-inverse .btn.btn-outline-dark:active, .bg-inverse .btn.btn-outline-dark:focus, .bg-inverse .btn.btn-outline-dark:hover, .open > .bg-inverse .btn-outline-dark.dropdown-toggle.custom-file-control:before, .open > .bg-inverse .btn.btn-outline-dark.dropdown-toggle { - color: #424242; - background-color: hsla(0, 0%, 80%, .15); - border-color: hsla(0, 0%, 80%, .15) -} - -.bg-inverse .btn-outline-dark.active.custom-file-control:focus:before, .bg-inverse .btn-outline-dark.active.custom-file-control:hover:before, .bg-inverse .btn-outline-dark.active.focus.custom-file-control:before, .bg-inverse .btn-outline-dark.custom-file-control:active.focus:before, .bg-inverse .btn-outline-dark.custom-file-control:active:focus:before, .bg-inverse .btn-outline-dark.custom-file-control:active:hover:before, .bg-inverse .btn.btn-outline-dark.active.focus, .bg-inverse .btn.btn-outline-dark.active:focus, .bg-inverse .btn.btn-outline-dark.active:hover, .bg-inverse .btn.btn-outline-dark:active.focus, .bg-inverse .btn.btn-outline-dark:active:focus, .bg-inverse .btn.btn-outline-dark:active:hover, .open > .bg-inverse .btn-outline-dark.dropdown-toggle.custom-file-control:focus:before, .open > .bg-inverse .btn-outline-dark.dropdown-toggle.custom-file-control:hover:before, .open > .bg-inverse .btn-outline-dark.dropdown-toggle.focus.custom-file-control:before, .open > .bg-inverse .btn.btn-outline-dark.dropdown-toggle.focus, .open > .bg-inverse .btn.btn-outline-dark.dropdown-toggle:focus, .open > .bg-inverse .btn.btn-outline-dark.dropdown-toggle:hover { - color: #424242; - background-color: hsla(0, 0%, 80%, .25); - border-color: hsla(0, 0%, 80%, .25) -} - -.open > .bg-inverse .btn-outline-dark.dropdown-toggle.bmd-btn-icon.custom-file-control:before, .open > .bg-inverse .btn.btn-outline-dark.dropdown-toggle.bmd-btn-icon { - color: inherit; - background-color: transparent -} - -.open > .bg-inverse .btn-outline-dark.dropdown-toggle.bmd-btn-icon.custom-file-control:hover:before, .open > .bg-inverse .btn.btn-outline-dark.dropdown-toggle.bmd-btn-icon:hover { - background-color: hsla(0, 0%, 80%, .15) -} - -.bg-inverse .btn-outline-dark.custom-file-control:disabled.focus:before, .bg-inverse .btn-outline-dark.custom-file-control:disabled:focus:before, .bg-inverse .btn-outline-dark.custom-file-control:disabled:hover:before, .bg-inverse .btn-outline-dark.disabled.custom-file-control:focus:before, .bg-inverse .btn-outline-dark.disabled.custom-file-control:hover:before, .bg-inverse .btn-outline-dark.disabled.focus.custom-file-control:before, .bg-inverse .btn.btn-outline-dark.disabled.focus, .bg-inverse .btn.btn-outline-dark.disabled:focus, .bg-inverse .btn.btn-outline-dark.disabled:hover, .bg-inverse .btn.btn-outline-dark:disabled.focus, .bg-inverse .btn.btn-outline-dark:disabled:focus, .bg-inverse .btn.btn-outline-dark:disabled:hover { - background-color: transparent; - border-color: #424242 -} - -.btn-outline-dark.btn-link.custom-file-control:before, .btn.btn-outline-dark.btn-link { - background-color: transparent -} - -.bmd-btn-fab.custom-file-control:before, .bmd-btn-icon.custom-file-control:before, .btn.bmd-btn-fab, .btn.bmd-btn-icon { - overflow: hidden; - font-size: 1.5rem; - line-height: 0 -} - -.bmd-btn-fab.custom-file-control:before, .bmd-btn-icon.custom-file-control:before, .btn-group-lg .bmd-btn-fab.custom-file-control:before, .btn-group-lg .bmd-btn-icon.custom-file-control:before, .btn-group-lg .btn.bmd-btn-fab, .btn-group-lg .btn.bmd-btn-icon, .btn-group-sm .bmd-btn-fab.custom-file-control:before, .btn-group-sm .bmd-btn-icon.custom-file-control:before, .btn-group-sm .btn.bmd-btn-fab, .btn-group-sm .btn.bmd-btn-icon, .btn.bmd-btn-fab, .btn.bmd-btn-icon { - padding: 0; - border-radius: 50%; - line-height: 0 -} - -.bmd-btn-fab.custom-file-control:before .material-icons, .bmd-btn-icon.custom-file-control:before .material-icons, .btn.bmd-btn-fab .material-icons, .btn.bmd-btn-icon .material-icons { - position: absolute; - top: 50%; - left: 50%; - width: 1.5rem; - line-height: 1.5rem; - transform: translate(-.75rem, -.75rem) -} - -.bmd-btn-fab.custom-file-control:before, .btn.bmd-btn-fab { - width: 3.5rem; - min-width: 3.5rem; - height: 3.5rem; - box-shadow: 0 1px 1.5px 0 rgba(0, 0, 0, .12), 0 1px 1px 0 rgba(0, 0, 0, .26) -} - -.bmd-btn-fab.custom-file-control:before .ripple-container, .btn.bmd-btn-fab .ripple-container { - border-radius: 50% -} - -.bmd-btn-fab.bmd-btn-fab-sm.custom-file-control:before, .btn-group-sm .bmd-btn-fab.custom-file-control:before, .btn-group-sm .btn.bmd-btn-fab, .btn.bmd-btn-fab.bmd-btn-fab-sm { - width: 2.5rem; - min-width: 2.5rem; - height: 2.5rem -} - -.bmd-btn-icon.custom-file-control:before, .btn.bmd-btn-icon { - width: 2rem; - min-width: 2rem; - height: 2rem; - margin: 0; - color: inherit; - line-height: 0 -} - -.bmd-btn-icon.bmd-btn-icon-sm.custom-file-control:before, .btn-group-sm .bmd-btn-icon.custom-file-control:before, .btn-group-sm .btn.bmd-btn-icon, .btn.bmd-btn-icon.bmd-btn-icon-sm { - width: 1.5rem; - min-width: 1.5rem; - height: 1.5rem; - font-size: 1.125rem -} - -.bmd-btn-icon.bmd-btn-icon-sm.custom-file-control:before .material-icons, .btn-group-sm .bmd-btn-icon.custom-file-control:before .material-icons, .btn-group-sm .btn.bmd-btn-icon .material-icons, .btn.bmd-btn-icon.bmd-btn-icon-sm .material-icons { - width: 1.125rem; - font-size: 1.125rem; - line-height: 1; - vertical-align: middle; - transform: translate(-.5625rem, -.5625rem) -} - -.btn .material-icons, .custom-file-control:before .material-icons { - position: relative; - display: inline-block; - top: .25em; - margin-top: -1em; - margin-bottom: -1em; - font-size: 1.5em; - vertical-align: middle -} - -.btn-group-lg .btn, .btn-group-lg .custom-file-control:before, .btn-group-lg > .btn, .btn-group-lg > .custom-file-control:before, .btn-lg.custom-file-control:before, .btn.btn-lg { - padding: .5rem 1rem; - font-size: 1.25rem; - line-height: 1.5; - border-radius: .3rem -} - -.btn-group-sm .btn, .btn-group-sm .custom-file-control:before, .btn-group-sm > .btn, .btn-group-sm > .custom-file-control:before, .btn-sm.custom-file-control:before, .btn.btn-sm { - padding: .40625rem .5rem; - line-height: 1.5; - border-radius: .0625rem; - font-size: .8125rem -} - -.btn-group-vertical.disabled, .btn-group-vertical:disabled, .btn-group-vertical[disabled], .btn-group.disabled, .btn-group:disabled, .btn-group[disabled], .btn.disabled, .btn:disabled, .btn[disabled], .custom-file-control:disabled:before, .disabled.custom-file-control:before, .input-group-btn .btn.disabled, .input-group-btn .btn:disabled, .input-group-btn .btn[disabled], .input-group-btn .custom-file-control:disabled:before, .input-group-btn .disabled.custom-file-control:before, .input-group-btn [disabled].custom-file-control:before, [disabled].custom-file-control:before, fieldset[disabled][disabled] .btn, fieldset[disabled][disabled] .btn-group, fieldset[disabled][disabled] .btn-group-vertical, fieldset[disabled][disabled] .custom-file-control:before, fieldset[disabled][disabled] .input-group-btn .btn, fieldset[disabled][disabled] .input-group-btn .custom-file-control:before { - color: rgba(0, 0, 0, .26) -} - -.bg-inverse .btn-group-vertical.disabled, .bg-inverse .btn-group-vertical:disabled, .bg-inverse .btn-group-vertical[disabled], .bg-inverse .btn-group.disabled, .bg-inverse .btn-group:disabled, .bg-inverse .btn-group[disabled], .bg-inverse .btn.disabled, .bg-inverse .btn:disabled, .bg-inverse .btn[disabled], .bg-inverse .custom-file-control:disabled:before, .bg-inverse .disabled.custom-file-control:before, .bg-inverse .input-group-btn .btn.disabled, .bg-inverse .input-group-btn .btn:disabled, .bg-inverse .input-group-btn .btn[disabled], .bg-inverse .input-group-btn .custom-file-control:disabled:before, .bg-inverse .input-group-btn .disabled.custom-file-control:before, .bg-inverse .input-group-btn [disabled].custom-file-control:before, .bg-inverse [disabled].custom-file-control:before, .bg-inverse fieldset[disabled][disabled] .btn, .bg-inverse fieldset[disabled][disabled] .btn-group, .bg-inverse fieldset[disabled][disabled] .btn-group-vertical, .bg-inverse fieldset[disabled][disabled] .custom-file-control:before, .bg-inverse fieldset[disabled][disabled] .input-group-btn .btn, .bg-inverse fieldset[disabled][disabled] .input-group-btn .custom-file-control:before { - color: hsla(0, 0%, 100%, .3) -} - -.btn-group-vertical.disabled, .btn-group-vertical.disabled:focus, .btn-group-vertical.disabled:hover, .btn-group-vertical:disabled, .btn-group-vertical:disabled:focus, .btn-group-vertical:disabled:hover, .btn-group-vertical[disabled], .btn-group-vertical[disabled]:focus, .btn-group-vertical[disabled]:hover, .btn-group.disabled, .btn-group.disabled:focus, .btn-group.disabled:hover, .btn-group:disabled, .btn-group:disabled:focus, .btn-group:disabled:hover, .btn-group[disabled], .btn-group[disabled]:focus, .btn-group[disabled]:hover, .btn.disabled, .btn.disabled:focus, .btn.disabled:hover, .btn:disabled, .btn:disabled:focus, .btn:disabled:hover, .btn[disabled], .btn[disabled]:focus, .btn[disabled]:hover, .custom-file-control:disabled:before, .custom-file-control:disabled:focus:before, .custom-file-control:disabled:hover:before, .disabled.custom-file-control:before, .disabled.custom-file-control:focus:before, .disabled.custom-file-control:hover:before, .input-group-btn .btn.disabled, .input-group-btn .btn.disabled:focus, .input-group-btn .btn.disabled:hover, .input-group-btn .btn:disabled, .input-group-btn .btn:disabled:focus, .input-group-btn .btn:disabled:hover, .input-group-btn .btn[disabled], .input-group-btn .btn[disabled]:focus, .input-group-btn .btn[disabled]:hover, .input-group-btn .custom-file-control:disabled:before, .input-group-btn .custom-file-control:disabled:focus:before, .input-group-btn .custom-file-control:disabled:hover:before, .input-group-btn .disabled.custom-file-control:before, .input-group-btn .disabled.custom-file-control:focus:before, .input-group-btn .disabled.custom-file-control:hover:before, .input-group-btn [disabled].custom-file-control:before, .input-group-btn [disabled].custom-file-control:focus:before, .input-group-btn [disabled].custom-file-control:hover:before, [disabled].custom-file-control:before, [disabled].custom-file-control:focus:before, [disabled].custom-file-control:hover:before, fieldset[disabled][disabled] .btn, fieldset[disabled][disabled] .btn-group, fieldset[disabled][disabled] .btn-group-vertical, fieldset[disabled][disabled] .btn-group-vertical:focus, fieldset[disabled][disabled] .btn-group-vertical:hover, fieldset[disabled][disabled] .btn-group:focus, fieldset[disabled][disabled] .btn-group:hover, fieldset[disabled][disabled] .btn:focus, fieldset[disabled][disabled] .btn:hover, fieldset[disabled][disabled] .custom-file-control:before, fieldset[disabled][disabled] .custom-file-control:focus:before, fieldset[disabled][disabled] .custom-file-control:hover:before, fieldset[disabled][disabled] .input-group-btn .btn, fieldset[disabled][disabled] .input-group-btn .btn:focus, fieldset[disabled][disabled] .input-group-btn .btn:hover, fieldset[disabled][disabled] .input-group-btn .custom-file-control:before, fieldset[disabled][disabled] .input-group-btn .custom-file-control:focus:before, fieldset[disabled][disabled] .input-group-btn .custom-file-control:hover:before { - background: transparent -} - -.btn-group, .btn-group-vertical { - position: relative; - margin: 10px 1px -} - -.btn-group-vertical .dropdown-menu, .btn-group .dropdown-menu { - border-radius: 0 0 .125rem .125rem -} - -.btn-group-vertical.btn-group-raised, .btn-group.btn-group-raised { - box-shadow: 0 2px 2px 0 rgba(0, 0, 0, .14), 0 3px 1px -2px rgba(0, 0, 0, .2), 0 1px 5px 0 rgba(0, 0, 0, .12) -} - -.btn-group-vertical .btn, .btn-group-vertical .btn + .btn, .btn-group-vertical .btn + .custom-file-control:before, .btn-group-vertical .btn-group, .btn-group-vertical .btn:active, .btn-group-vertical .custom-file-control:active:before, .btn-group-vertical .custom-file-control:before, .btn-group-vertical .custom-file-control:before + .btn, .btn-group-vertical .custom-file-control:before + .custom-file-control:before, .btn-group-vertical > .btn-group, .btn-group .btn, .btn-group .btn + .btn, .btn-group .btn + .custom-file-control:before, .btn-group .btn-group, .btn-group .btn:active, .btn-group .custom-file-control:active:before, .btn-group .custom-file-control:before, .btn-group .custom-file-control:before + .btn, .btn-group .custom-file-control:before + .custom-file-control:before, .btn-group > .btn-group { - margin: 0 -} - -.checkbox label, label.checkbox-inline { - position: relative; - padding-left: 1.5625rem -} - -.checkbox label .checkbox-decorator, label.checkbox-inline .checkbox-decorator { - position: absolute; - left: 0; - padding: .7em; - margin: -.7em; - line-height: .7; - vertical-align: middle; - cursor: pointer; - border-radius: 100% -} - -.checkbox label .checkbox-decorator .check, label.checkbox-inline .checkbox-decorator .check { - position: relative; - z-index: 1; - display: inline-block; - width: 1.25rem; - height: 1.25rem; - overflow: hidden; - border: .125rem solid rgba(0, 0, 0, .54); - border-radius: .125rem -} - -.checkbox label .checkbox-decorator .check:before, label.checkbox-inline .checkbox-decorator .check:before { - position: absolute; - display: block; - width: 0; - height: 0; - margin-top: -4px; - margin-left: 6px; - color: #757575; - content: ""; - box-shadow: 0 0 0 0, 0 0 0 0, 0 0 0 0, 0 0 0 0, 0 0 0 0, 0 0 0 0, inset 0 0 0 0; - transform: rotate(45deg); - animation: c -} - -.is-focused .checkbox label .checkbox-decorator .check:before, .is-focused label.checkbox-inline .checkbox-decorator .check:before { - animation: c .3s forwards -} - -.checkbox label input[type=checkbox], label.checkbox-inline input[type=checkbox] { - position: absolute; - left: 0; - z-index: -1; - width: 0; - height: 0; - margin: 0; - overflow: hidden; - pointer-events: none; - opacity: 0 -} - -.checkbox label input[type=checkbox]:focus + .checkbox-decorator .check:after, label.checkbox-inline input[type=checkbox]:focus + .checkbox-decorator .check:after { - opacity: .2 -} - -.checkbox label input[type=checkbox]:checked + .checkbox-decorator .check, label.checkbox-inline input[type=checkbox]:checked + .checkbox-decorator .check { - color: #009688; - border-color: #009688 -} - -.checkbox label input[type=checkbox]:checked + .checkbox-decorator .check:before, label.checkbox-inline input[type=checkbox]:checked + .checkbox-decorator .check:before { - color: #009688; - box-shadow: 0 0 0 10px, 10px -10px 0 10px, 32px 0 0 20px, 32px 0 20px, -5px 5px 0 10px, 20px -12px 0 11px; - animation: b .3s forwards -} - -.checkbox label input[type=checkbox][disabled] + .checkbox-decorator .check:after, .checkbox label input[type=checkbox][disabled] .check:after, fieldset[disabled] .checkbox label input[type=checkbox] + .checkbox-decorator .check:after, fieldset[disabled] .checkbox label input[type=checkbox] .check:after, fieldset[disabled] label.checkbox-inline input[type=checkbox] + .checkbox-decorator .check:after, fieldset[disabled] label.checkbox-inline input[type=checkbox] .check:after, label.checkbox-inline input[type=checkbox][disabled] + .checkbox-decorator .check:after, label.checkbox-inline input[type=checkbox][disabled] .check:after { - background-color: rgba(0, 0, 0, .87); - transform: rotate(-45deg) -} - -.checkbox label input[type=checkbox][disabled] + .checkbox-decorator .check, .checkbox label input[type=checkbox][disabled] .check, fieldset[disabled] .checkbox label input[type=checkbox] + .checkbox-decorator .check, fieldset[disabled] .checkbox label input[type=checkbox] .check, fieldset[disabled] label.checkbox-inline input[type=checkbox] + .checkbox-decorator .check, fieldset[disabled] label.checkbox-inline input[type=checkbox] .check, label.checkbox-inline input[type=checkbox][disabled] + .checkbox-decorator .check, label.checkbox-inline input[type=checkbox][disabled] .check { - border-color: rgba(0, 0, 0, .26) -} - -.checkbox label input[type=checkbox][disabled] + .checkbox-decorator .ripple-container, fieldset[disabled] .checkbox label input[type=checkbox] + .checkbox-decorator .ripple-container, fieldset[disabled] label.checkbox-inline input[type=checkbox] + .checkbox-decorator .ripple-container, label.checkbox-inline input[type=checkbox][disabled] + .checkbox-decorator .ripple-container { - display: none -} - -@keyframes b { - 0% { - box-shadow: 0 0 0 10px, 10px -10px 0 10px, 32px 0 0 20px, 0 32px 0 20px, -5px 5px 0 10px, 15px 2px 0 11px - } - 50% { - box-shadow: 0 0 0 10px, 10px -10px 0 10px, 32px 0 0 20px, 0 32px 0 20px, -5px 5px 0 10px, 20px 2px 0 11px - } - to { - box-shadow: 0 0 0 10px, 10px -10px 0 10px, 32px 0 0 20px, 0 32px 0 20px, -5px 5px 0 10px, 20px -12px 0 11px - } -} - -@keyframes c { - 0% { - box-shadow: 0 0 0 10px, 10px -10px 0 10px, 32px 0 0 20px, 0 32px 0 20px, -5px 5px 0 10px, 20px -12px 0 11px, inset 0 0 0 0 - } - 25% { - box-shadow: 0 0 0 10px, 10px -10px 0 10px, 32px 0 0 20px, 0 32px 0 20px, -5px 5px 0 10px, 20px -12px 0 11px, inset 0 0 0 0 - } - 50% { - width: 0; - height: 0; - margin-top: -4px; - margin-left: 6px; - box-shadow: 0 0 0 10px, 10px -10px 0 10px, 32px 0 0 20px, 0 32px 0 20px, -5px 5px 0 10px, 15px 2px 0 11px, inset 0 0 0 0; - transform: rotate(45deg) - } - 51% { - width: 20px; - height: 20px; - margin-top: -2px; - margin-left: -2px; - box-shadow: 0 0 0 0, 0 0 0 0, 0 0 0 0, 0 0 0 0, 0 0 0 0, 0 0 0 0, inset 0 0 0 10px; - transform: rotate(0deg) - } - to { - width: 20px; - height: 20px; - margin-top: -2px; - margin-left: -2px; - box-shadow: 0 0 0 0, 0 0 0 0, 0 0 0 0, 0 0 0 0, 0 0 0 0, 0 0 0 0, inset 0 0 0 0; - transform: rotate(0deg) - } -} - -.custom-file-control [class*=" bmd-label"], .custom-file-control [class^=bmd-label] { - color: rgba(0, 0, 0, .26) -} - -.custom-file-control .custom-file-control, .custom-file-control .form-control, .is-focused .custom-file-control .custom-file-control, .is-focused .custom-file-control .form-control { - background-image: linear-gradient(0deg, #009688 2px, rgba(0, 150, 136, 0) 0), linear-gradient(0deg, rgba(0, 0, 0, .26) 1px, transparent 0) -} - -.custom-file-control .custom-file-control:invalid, .custom-file-control .form-control:invalid { - background-image: linear-gradient(0deg, #d50000 2px, rgba(213, 0, 0, 0) 0), linear-gradient(0deg, rgba(0, 0, 0, .26) 1px, transparent 0) -} - -.custom-file-control .custom-file-control:read-only, .custom-file-control .form-control:read-only { - background-image: linear-gradient(0deg, rgba(0, 0, 0, .26) 1px, transparent 0), linear-gradient(0deg, rgba(0, 0, 0, .26) 1px, transparent 0) -} - -.custom-file-control .custom-file-control:disabled, .custom-file-control .disabled.custom-file-control, .custom-file-control .form-control.disabled, .custom-file-control .form-control:disabled, .custom-file-control .form-control[disabled], .custom-file-control [disabled].custom-file-control, fieldset[disabled][disabled] .custom-file-control .custom-file-control, fieldset[disabled][disabled] .custom-file-control .form-control { - background-image: linear-gradient(90deg, rgba(0, 0, 0, .26) 0, rgba(0, 0, 0, .26) 30%, transparent 0, transparent); - background-repeat: repeat-x; - background-size: 3px 1px -} - -.custom-file-control .form-control-success.custom-file-control, .custom-file-control .form-control.form-control-success, .is-focused .custom-file-control .form-control-success.custom-file-control, .is-focused .custom-file-control .form-control.form-control-success { - background-image: linear-gradient(0deg, #009688 2px, rgba(0, 150, 136, 0) 0), linear-gradient(0deg, rgba(0, 0, 0, .26) 1px, transparent 0), "data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCA2MTIgNzkyIj48cGF0aCBmaWxsPSIjNWNiODVjIiBkPSJNMjMzLjggNjEwYy0xMy4zIDAtMjYtNi0zNC0xNi44TDkwLjUgNDQ4LjhDNzYuMyA0MzAgODAgNDAzLjMgOTguOCAzODljMTguOC0xNC4yIDQ1LjUtMTAuNCA1OS44IDguNGw3MiA5NUw0NTEuMyAyNDJjMTIuNS0yMCAzOC44LTI2LjIgNTguOC0xMy43IDIwIDEyLjQgMjYgMzguNyAxMy43IDU4LjhMMjcwIDU5MGMtNy40IDEyLTIwLjIgMTkuNC0zNC4zIDIwaC0yeiIvPjwvc3ZnPg==" -} - -.custom-file-control .form-control-warning.custom-file-control, .custom-file-control .form-control.form-control-warning, .is-focused .custom-file-control .form-control-warning.custom-file-control, .is-focused .custom-file-control .form-control.form-control-warning { - background-image: linear-gradient(0deg, #009688 2px, rgba(0, 150, 136, 0) 0), linear-gradient(0deg, rgba(0, 0, 0, .26) 1px, transparent 0), "data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCA2MTIgNzkyIj48cGF0aCBmaWxsPSIjZjBhZDRlIiBkPSJNNjAzIDY0MC4ybC0yNzguNS01MDljLTMuOC02LjYtMTAuOC0xMC42LTE4LjUtMTAuNnMtMTQuNyA0LTE4LjUgMTAuNkw5IDY0MC4yYy0zLjcgNi41LTMuNiAxNC40LjIgMjAuOCAzLjggNi41IDEwLjggMTAuNCAxOC4zIDEwLjRoNTU3YzcuNiAwIDE0LjYtNCAxOC40LTEwLjQgMy41LTYuNCAzLjYtMTQuNCAwLTIwLjh6bS0yNjYuNC0zMGgtNjEuMlY1NDloNjEuMnY2MS4yem0wLTEwN2gtNjEuMlYzMDRoNjEuMnYxOTl6Ii8+PC9zdmc+" -} - -.custom-file-control .form-control-danger.custom-file-control, .custom-file-control .form-control.form-control-danger, .is-focused .custom-file-control .form-control-danger.custom-file-control, .is-focused .custom-file-control .form-control.form-control-danger { - background-image: linear-gradient(0deg, #009688 2px, rgba(0, 150, 136, 0) 0), linear-gradient(0deg, rgba(0, 0, 0, .26) 1px, transparent 0), "data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCA2MTIgNzkyIj48cGF0aCBmaWxsPSIjZDk1MzRmIiBkPSJNNDQ3IDU0NC40Yy0xNC40IDE0LjQtMzcuNiAxNC40LTUyIDBsLTg5LTkyLjctODkgOTIuN2MtMTQuNSAxNC40LTM3LjcgMTQuNC01MiAwLTE0LjQtMTQuNC0xNC40LTM3LjYgMC01Mmw5Mi40LTk2LjMtOTIuNC05Ni4zYy0xNC40LTE0LjQtMTQuNC0zNy42IDAtNTJzMzcuNi0xNC4zIDUyIDBsODkgOTIuOCA4OS4yLTkyLjdjMTQuNC0xNC40IDM3LjYtMTQuNCA1MiAwIDE0LjMgMTQuNCAxNC4zIDM3LjYgMCA1MkwzNTQuNiAzOTZsOTIuNCA5Ni40YzE0LjQgMTQuNCAxNC40IDM3LjYgMCA1MnoiLz48L3N2Zz4=" -} - -.custom-file-control.is-focused .valid-feedback, .custom-file-control .is-focused .valid-feedback { - display: none; - width: 100%; - margin-top: .25rem; - font-size: 80%; - color: rgba(0, 0, 0, .26) -} - -.custom-file-control.is-focused .valid-tooltip, .custom-file-control .is-focused .valid-tooltip { - position: absolute; - top: 100%; - z-index: 5; - display: none; - max-width: 100%; - padding: .5rem; - margin-top: .1rem; - font-size: .875rem; - line-height: 1; - color: #fff; - background-color: rgba(0, 0, 0, .8); - border-radius: .2rem -} - -.custom-file-control.is-focused .custom-select.is-valid, .custom-file-control .is-focused .custom-select.is-valid, .custom-file-control.is-focused .form-control.is-valid, .custom-file-control .is-focused .form-control.is-valid, .custom-file-control.is-focused .is-valid.custom-file-control, .custom-file-control .is-focused .is-valid.custom-file-control, .was-validated .custom-file-control.is-focused .custom-file-control:valid, .was-validated .custom-file-control .is-focused .custom-file-control:valid, .was-validated .custom-file-control.is-focused .custom-select:valid, .was-validated .custom-file-control .is-focused .custom-select:valid, .was-validated .custom-file-control.is-focused .form-control:valid, .was-validated .custom-file-control .is-focused .form-control:valid { - border-color: rgba(0, 0, 0, .26) -} - -.custom-file-control.is-focused .custom-select.is-valid:focus, .custom-file-control .is-focused .custom-select.is-valid:focus, .custom-file-control.is-focused .form-control.is-valid:focus, .custom-file-control .is-focused .form-control.is-valid:focus, .custom-file-control.is-focused .is-valid.custom-file-control:focus, .custom-file-control .is-focused .is-valid.custom-file-control:focus, .was-validated .custom-file-control.is-focused .custom-file-control:valid:focus, .was-validated .custom-file-control .is-focused .custom-file-control:valid:focus, .was-validated .custom-file-control.is-focused .custom-select:valid:focus, .was-validated .custom-file-control .is-focused .custom-select:valid:focus, .was-validated .custom-file-control.is-focused .form-control:valid:focus, .was-validated .custom-file-control .is-focused .form-control:valid:focus { - border-color: rgba(0, 0, 0, .26); - box-shadow: 0 0 0 .2rem rgba(0, 0, 0, .25) -} - -.custom-file-control.is-focused .custom-select.is-valid ~ .valid-feedback, .custom-file-control .is-focused .custom-select.is-valid ~ .valid-feedback, .custom-file-control.is-focused .custom-select.is-valid ~ .valid-tooltip, .custom-file-control .is-focused .custom-select.is-valid ~ .valid-tooltip, .custom-file-control.is-focused .form-control.is-valid ~ .valid-feedback, .custom-file-control .is-focused .form-control.is-valid ~ .valid-feedback, .custom-file-control.is-focused .form-control.is-valid ~ .valid-tooltip, .custom-file-control .is-focused .form-control.is-valid ~ .valid-tooltip, .custom-file-control.is-focused .is-valid.custom-file-control ~ .valid-feedback, .custom-file-control .is-focused .is-valid.custom-file-control ~ .valid-feedback, .custom-file-control.is-focused .is-valid.custom-file-control ~ .valid-tooltip, .custom-file-control .is-focused .is-valid.custom-file-control ~ .valid-tooltip, .was-validated .custom-file-control.is-focused .custom-file-control:valid ~ .valid-feedback, .was-validated .custom-file-control .is-focused .custom-file-control:valid ~ .valid-feedback, .was-validated .custom-file-control.is-focused .custom-file-control:valid ~ .valid-tooltip, .was-validated .custom-file-control .is-focused .custom-file-control:valid ~ .valid-tooltip, .was-validated .custom-file-control.is-focused .custom-select:valid ~ .valid-feedback, .was-validated .custom-file-control .is-focused .custom-select:valid ~ .valid-feedback, .was-validated .custom-file-control.is-focused .custom-select:valid ~ .valid-tooltip, .was-validated .custom-file-control .is-focused .custom-select:valid ~ .valid-tooltip, .was-validated .custom-file-control.is-focused .form-control:valid ~ .valid-feedback, .was-validated .custom-file-control .is-focused .form-control:valid ~ .valid-feedback, .was-validated .custom-file-control.is-focused .form-control:valid ~ .valid-tooltip, .was-validated .custom-file-control .is-focused .form-control:valid ~ .valid-tooltip { - display: block -} - -.custom-file-control.is-focused .form-check-input.is-valid ~ .form-check-label, .custom-file-control .is-focused .form-check-input.is-valid ~ .form-check-label, .was-validated .custom-file-control.is-focused .form-check-input:valid ~ .form-check-label, .was-validated .custom-file-control .is-focused .form-check-input:valid ~ .form-check-label { - color: rgba(0, 0, 0, .26) -} - -.custom-file-control.is-focused .form-check-input.is-valid ~ .valid-feedback, .custom-file-control .is-focused .form-check-input.is-valid ~ .valid-feedback, .custom-file-control.is-focused .form-check-input.is-valid ~ .valid-tooltip, .custom-file-control .is-focused .form-check-input.is-valid ~ .valid-tooltip, .was-validated .custom-file-control.is-focused .form-check-input:valid ~ .valid-feedback, .was-validated .custom-file-control .is-focused .form-check-input:valid ~ .valid-feedback, .was-validated .custom-file-control.is-focused .form-check-input:valid ~ .valid-tooltip, .was-validated .custom-file-control .is-focused .form-check-input:valid ~ .valid-tooltip { - display: block -} - -.custom-file-control.is-focused .custom-control-input.is-valid ~ .custom-control-label, .custom-file-control .is-focused .custom-control-input.is-valid ~ .custom-control-label, .was-validated .custom-file-control.is-focused .custom-control-input:valid ~ .custom-control-label, .was-validated .custom-file-control .is-focused .custom-control-input:valid ~ .custom-control-label { - color: rgba(0, 0, 0, .26) -} - -.custom-file-control.is-focused .custom-control-input.is-valid ~ .custom-control-label:before, .custom-file-control .is-focused .custom-control-input.is-valid ~ .custom-control-label:before, .was-validated .custom-file-control.is-focused .custom-control-input:valid ~ .custom-control-label:before, .was-validated .custom-file-control .is-focused .custom-control-input:valid ~ .custom-control-label:before { - background-color: rgba(64, 64, 64, .26) -} - -.custom-file-control.is-focused .custom-control-input.is-valid ~ .valid-feedback, .custom-file-control .is-focused .custom-control-input.is-valid ~ .valid-feedback, .custom-file-control.is-focused .custom-control-input.is-valid ~ .valid-tooltip, .custom-file-control .is-focused .custom-control-input.is-valid ~ .valid-tooltip, .was-validated .custom-file-control.is-focused .custom-control-input:valid ~ .valid-feedback, .was-validated .custom-file-control .is-focused .custom-control-input:valid ~ .valid-feedback, .was-validated .custom-file-control.is-focused .custom-control-input:valid ~ .valid-tooltip, .was-validated .custom-file-control .is-focused .custom-control-input:valid ~ .valid-tooltip { - display: block -} - -.custom-file-control.is-focused .custom-control-input.is-valid:checked ~ .custom-control-label:before, .custom-file-control .is-focused .custom-control-input.is-valid:checked ~ .custom-control-label:before, .was-validated .custom-file-control.is-focused .custom-control-input:valid:checked ~ .custom-control-label:before, .was-validated .custom-file-control .is-focused .custom-control-input:valid:checked ~ .custom-control-label:before { - background-color: rgba(26, 26, 26, .26) -} - -.custom-file-control.is-focused .custom-control-input.is-valid:focus ~ .custom-control-label:before, .custom-file-control .is-focused .custom-control-input.is-valid:focus ~ .custom-control-label:before, .was-validated .custom-file-control.is-focused .custom-control-input:valid:focus ~ .custom-control-label:before, .was-validated .custom-file-control .is-focused .custom-control-input:valid:focus ~ .custom-control-label:before { - box-shadow: 0 0 0 1px #fafafa, 0 0 0 .2rem rgba(0, 0, 0, .25) -} - -.custom-file-control.is-focused .custom-file-input.is-valid ~ .custom-file-label, .custom-file-control .is-focused .custom-file-input.is-valid ~ .custom-file-label, .was-validated .custom-file-control.is-focused .custom-file-input:valid ~ .custom-file-label, .was-validated .custom-file-control .is-focused .custom-file-input:valid ~ .custom-file-label { - border-color: rgba(0, 0, 0, .26) -} - -.custom-file-control.is-focused .custom-file-input.is-valid ~ .custom-file-label:before, .custom-file-control .is-focused .custom-file-input.is-valid ~ .custom-file-label:before, .was-validated .custom-file-control.is-focused .custom-file-input:valid ~ .custom-file-label:before, .was-validated .custom-file-control .is-focused .custom-file-input:valid ~ .custom-file-label:before { - border-color: inherit -} - -.custom-file-control.is-focused .custom-file-input.is-valid ~ .valid-feedback, .custom-file-control .is-focused .custom-file-input.is-valid ~ .valid-feedback, .custom-file-control.is-focused .custom-file-input.is-valid ~ .valid-tooltip, .custom-file-control .is-focused .custom-file-input.is-valid ~ .valid-tooltip, .was-validated .custom-file-control.is-focused .custom-file-input:valid ~ .valid-feedback, .was-validated .custom-file-control .is-focused .custom-file-input:valid ~ .valid-feedback, .was-validated .custom-file-control.is-focused .custom-file-input:valid ~ .valid-tooltip, .was-validated .custom-file-control .is-focused .custom-file-input:valid ~ .valid-tooltip { - display: block -} - -.custom-file-control.is-focused .custom-file-input.is-valid:focus ~ .custom-file-label, .custom-file-control .is-focused .custom-file-input.is-valid:focus ~ .custom-file-label, .was-validated .custom-file-control.is-focused .custom-file-input:valid:focus ~ .custom-file-label, .was-validated .custom-file-control .is-focused .custom-file-input:valid:focus ~ .custom-file-label { - box-shadow: 0 0 0 .2rem rgba(0, 0, 0, .25) -} - -.custom-file-control.is-focused [class*=" bmd-label"], .custom-file-control .is-focused [class*=" bmd-label"], .custom-file-control.is-focused [class^=bmd-label], .custom-file-control .is-focused [class^=bmd-label] { - color: #009688 -} - -.custom-file-control.is-focused .bmd-label-placeholder, .custom-file-control .is-focused .bmd-label-placeholder { - color: rgba(0, 0, 0, .26) -} - -.custom-file-control.is-focused .custom-file-control, .custom-file-control .is-focused .custom-file-control, .custom-file-control.is-focused .form-control, .custom-file-control .is-focused .form-control { - border-color: rgba(0, 0, 0, .26) -} - -.custom-file-control.is-focused .bmd-help, .custom-file-control .is-focused .bmd-help { - color: rgba(0, 0, 0, .54) -} - -.custom-file-control:before { - position: absolute; - height: calc(100% - 1px) -} - -.switch label { - position: relative; - padding-left: 2.4375rem -} - -.switch label .bmd-switch-track { - position: absolute; - top: .3125rem; - left: 0; - display: inline-block; - width: 2.125rem; - height: .875rem; - cursor: pointer; - background-image: linear-gradient(90deg, rgba(0, 0, 0, .26) 0, rgba(0, 0, 0, .26) 50%, #49dcce 0, #49dcce); - background-position: 0; - background-size: 4.25rem .875rem; - border-radius: 2.125rem; - transition: background-position .2s ease-in -} - -.switch label .bmd-switch-track:after { - position: absolute; - top: 50%; - left: 0; - display: block; - align-self: center; - width: 1.25rem; - height: 1.25rem; - content: ""; - background: #f1f1f1; - border-radius: 100%; - box-shadow: 0 1px 3px rgba(0, 0, 0, .5); - transition: left .2s ease-in, background-color .2s ease-in, transform .3s ease; - transform: translateY(-50%) -} - -.switch label .bmd-switch-track:active:after { - transform: translateY(-50%) scale3d(1.15, .85, 1) -} - -.switch label input { - position: absolute; - display: block; - width: 0; - height: 0; - opacity: 0 -} - -.switch label input:checked + .bmd-switch-track { - background-position: -100% -} - -.switch label input:checked + .bmd-switch-track:after { - left: calc(100% - 1.25rem); - background-color: #009688 -} - -.switch label input:disabled + .bmd-switch-track:active:after { - transform: translateY(-50%) -} - -.switch label input:disabled + .bmd-switch-track { - cursor: default; - background: rgba(0, 0, 0, .12) -} - -.switch label input:disabled + .bmd-switch-track:after { - background: #bdbdbd -} - -.radio label, label.radio-inline { - position: relative; - padding-left: 1.5625rem -} - -.radio label .bmd-radio, label.radio-inline .bmd-radio { - position: absolute; - left: 0; - display: inline-block; - padding: .7em; - margin: -.7em; - line-height: .7; - border-radius: 100% -} - -.radio label .bmd-radio:after, label.radio-inline .bmd-radio:after { - display: inline-block; - width: 1.25rem; - height: 1.25rem; - cursor: pointer; - content: ""; - border: .125rem solid rgba(0, 0, 0, .54); - border-radius: 50%; - transition: border-color .28s ease; - transition-duration: .2s -} - -.radio label .bmd-radio:before, label.radio-inline .bmd-radio:before { - position: absolute; - display: inline-block; - width: 1.25rem; - height: 1.25rem; - content: ""; - background-color: #009688; - border-radius: 50%; - transition: transform .28s ease; - transform: scale3d(0, 0, 0) -} - -.radio label input[type=radio], label.radio-inline input[type=radio] { - position: absolute; - left: 0; - z-index: -1; - width: 0; - height: 0; - margin: 0; - overflow: hidden; - pointer-events: none; - opacity: 0 -} - -.radio label input[type=radio]:checked ~ .bmd-radio:after, label.radio-inline input[type=radio]:checked ~ .bmd-radio:after { - border-color: #009688 -} - -.radio label input[type=radio]:checked ~ .bmd-radio:before, label.radio-inline input[type=radio]:checked ~ .bmd-radio:before { - background-color: #009688; - transform: scale3d(.5, .5, 1) -} - -.radio label input[type=radio]:disabled + .bmd-radio, .radio label input[type=radio][disabled] + .bmd-radio, fieldset[disabled] .radio label input[type=radio] + .bmd-radio, fieldset[disabled] label.radio-inline input[type=radio] + .bmd-radio, label.radio-inline input[type=radio]:disabled + .bmd-radio, label.radio-inline input[type=radio][disabled] + .bmd-radio { - cursor: default -} - -.radio label input[type=radio]:disabled + .bmd-radio:after, .radio label input[type=radio][disabled] + .bmd-radio:after, fieldset[disabled] .radio label input[type=radio] + .bmd-radio:after, fieldset[disabled] label.radio-inline input[type=radio] + .bmd-radio:after, label.radio-inline input[type=radio]:disabled + .bmd-radio:after, label.radio-inline input[type=radio][disabled] + .bmd-radio:after { - border-color: rgba(0, 0, 0, .26) -} - -.radio label input[type=radio]:disabled + .bmd-radio:before, .radio label input[type=radio][disabled] + .bmd-radio:before, fieldset[disabled] .radio label input[type=radio] + .bmd-radio:before, fieldset[disabled] label.radio-inline input[type=radio] + .bmd-radio:before, label.radio-inline input[type=radio]:disabled + .bmd-radio:before, label.radio-inline input[type=radio][disabled] + .bmd-radio:before { - background-color: rgba(0, 0, 0, .26) -} - -.radio label input[type=radio]:disabled + .bmd-radio .ripple-container, .radio label input[type=radio][disabled] + .bmd-radio .ripple-container, fieldset[disabled] .radio label input[type=radio] + .bmd-radio .ripple-container, fieldset[disabled] label.radio-inline input[type=radio] + .bmd-radio .ripple-container, label.radio-inline input[type=radio]:disabled + .bmd-radio .ripple-container, label.radio-inline input[type=radio][disabled] + .bmd-radio .ripple-container { - display: none -} - -.bg-inverse .radio label input[type=radio]:disabled + .bmd-radio:after, .bg-inverse .radio label input[type=radio][disabled] + .bmd-radio:after, .bg-inverse fieldset[disabled] .radio label input[type=radio] + .bmd-radio:after, .bg-inverse fieldset[disabled] label.radio-inline input[type=radio] + .bmd-radio:after, .bg-inverse label.radio-inline input[type=radio]:disabled + .bmd-radio:after, .bg-inverse label.radio-inline input[type=radio][disabled] + .bmd-radio:after { - border-color: hsla(0, 0%, 100%, .3) -} - -.bg-inverse .radio label input[type=radio]:disabled + .bmd-radio:before, .bg-inverse .radio label input[type=radio][disabled] + .bmd-radio:before, .bg-inverse fieldset[disabled] .radio label input[type=radio] + .bmd-radio:before, .bg-inverse fieldset[disabled] label.radio-inline input[type=radio] + .bmd-radio:before, .bg-inverse label.radio-inline input[type=radio]:disabled + .bmd-radio:before, .bg-inverse label.radio-inline input[type=radio][disabled] + .bmd-radio:before { - background-color: hsla(0, 0%, 100%, .3) -} - -form { - margin-bottom: 1.125rem -} - -.navbar form { - margin-bottom: 0 -} - -.navbar form .bmd-form-group { - display: inline-block; - padding-top: 0 -} - -.navbar form .btn, .navbar form .custom-file-control:before { - margin-bottom: 0 -} - -.custom-file-control, .form-control { - background: no-repeat bottom, 50% calc(100% - 1px); - background-size: 0 100%, 100% 100%; - border: 0; - transition: background 0s ease-out; - padding-left: 0; - padding-right: 0 -} - -.bmd-form-group.is-focused .custom-file-control, .bmd-form-group.is-focused .form-control, .custom-file-control:focus, .form-control:focus { - background-size: 100% 100%, 100% 100%; - transition-duration: .3s -} - -.bmd-help { - position: absolute; - display: none; - font-size: .8rem; - font-weight: 400 -} - -.bmd-form-group.is-focused .bmd-help { - display: block -} - -.bmd-help:nth-of-type(2) { - padding-top: 1rem -} - -.bmd-help + .bmd-help { - position: relative; - margin-bottom: 0 -} - -.checkbox-inline, .checkbox label, .is-focused .checkbox-inline, .is-focused .checkbox label, .is-focused .radio-inline, .is-focused .radio label, .is-focused .switch label, .radio-inline, .radio label, .switch label { - color: rgba(0, 0, 0, .26) -} - -.checkbox-inline:active, .checkbox-inline:focus, .checkbox-inline:hover, .checkbox label:active, .checkbox label:focus, .checkbox label:hover, .is-focused .checkbox-inline:active, .is-focused .checkbox-inline:focus, .is-focused .checkbox-inline:hover, .is-focused .checkbox label:active, .is-focused .checkbox label:focus, .is-focused .checkbox label:hover, .is-focused .radio-inline:active, .is-focused .radio-inline:focus, .is-focused .radio-inline:hover, .is-focused .radio label:active, .is-focused .radio label:focus, .is-focused .radio label:hover, .is-focused .switch label:active, .is-focused .switch label:focus, .is-focused .switch label:hover, .radio-inline:active, .radio-inline:focus, .radio-inline:hover, .radio label:active, .radio label:focus, .radio label:hover, .switch label:active, .switch label:focus, .switch label:hover { - color: rgba(0, 0, 0, .54) -} - -.checkbox-inline label:has(input[type=checkbox][disabled]), .checkbox-inline label:has(input[type=checkbox][disabled]):focus, .checkbox-inline label:has(input[type=checkbox][disabled]):hover, .checkbox-inline label:has(input[type=radio][disabled]), .checkbox-inline label:has(input[type=radio][disabled]):focus, .checkbox-inline label:has(input[type=radio][disabled]):hover, .checkbox label label:has(input[type=checkbox][disabled]), .checkbox label label:has(input[type=checkbox][disabled]):focus, .checkbox label label:has(input[type=checkbox][disabled]):hover, .checkbox label label:has(input[type=radio][disabled]), .checkbox label label:has(input[type=radio][disabled]):focus, .checkbox label label:has(input[type=radio][disabled]):hover, .is-focused .checkbox-inline label:has(input[type=checkbox][disabled]), .is-focused .checkbox-inline label:has(input[type=checkbox][disabled]):focus, .is-focused .checkbox-inline label:has(input[type=checkbox][disabled]):hover, .is-focused .checkbox-inline label:has(input[type=radio][disabled]), .is-focused .checkbox-inline label:has(input[type=radio][disabled]):focus, .is-focused .checkbox-inline label:has(input[type=radio][disabled]):hover, .is-focused .checkbox label label:has(input[type=checkbox][disabled]), .is-focused .checkbox label label:has(input[type=checkbox][disabled]):focus, .is-focused .checkbox label label:has(input[type=checkbox][disabled]):hover, .is-focused .checkbox label label:has(input[type=radio][disabled]), .is-focused .checkbox label label:has(input[type=radio][disabled]):focus, .is-focused .checkbox label label:has(input[type=radio][disabled]):hover, .is-focused .radio-inline label:has(input[type=checkbox][disabled]), .is-focused .radio-inline label:has(input[type=checkbox][disabled]):focus, .is-focused .radio-inline label:has(input[type=checkbox][disabled]):hover, .is-focused .radio-inline label:has(input[type=radio][disabled]), .is-focused .radio-inline label:has(input[type=radio][disabled]):focus, .is-focused .radio-inline label:has(input[type=radio][disabled]):hover, .is-focused .radio label label:has(input[type=checkbox][disabled]), .is-focused .radio label label:has(input[type=checkbox][disabled]):focus, .is-focused .radio label label:has(input[type=checkbox][disabled]):hover, .is-focused .radio label label:has(input[type=radio][disabled]), .is-focused .radio label label:has(input[type=radio][disabled]):focus, .is-focused .radio label label:has(input[type=radio][disabled]):hover, .is-focused .switch label label:has(input[type=checkbox][disabled]), .is-focused .switch label label:has(input[type=checkbox][disabled]):focus, .is-focused .switch label label:has(input[type=checkbox][disabled]):hover, .is-focused .switch label label:has(input[type=radio][disabled]), .is-focused .switch label label:has(input[type=radio][disabled]):focus, .is-focused .switch label label:has(input[type=radio][disabled]):hover, .radio-inline label:has(input[type=checkbox][disabled]), .radio-inline label:has(input[type=checkbox][disabled]):focus, .radio-inline label:has(input[type=checkbox][disabled]):hover, .radio-inline label:has(input[type=radio][disabled]), .radio-inline label:has(input[type=radio][disabled]):focus, .radio-inline label:has(input[type=radio][disabled]):hover, .radio label label:has(input[type=checkbox][disabled]), .radio label label:has(input[type=checkbox][disabled]):focus, .radio label label:has(input[type=checkbox][disabled]):hover, .radio label label:has(input[type=radio][disabled]), .radio label label:has(input[type=radio][disabled]):focus, .radio label label:has(input[type=radio][disabled]):hover, .switch label label:has(input[type=checkbox][disabled]), .switch label label:has(input[type=checkbox][disabled]):focus, .switch label label:has(input[type=checkbox][disabled]):hover, .switch label label:has(input[type=radio][disabled]), .switch label label:has(input[type=radio][disabled]):focus, .switch label label:has(input[type=radio][disabled]):hover, fieldset[disabled] .checkbox-inline, fieldset[disabled] .checkbox-inline:focus, fieldset[disabled] .checkbox-inline:hover, fieldset[disabled] .checkbox label, fieldset[disabled] .checkbox label:focus, fieldset[disabled] .checkbox label:hover, fieldset[disabled] .is-focused .checkbox-inline, fieldset[disabled] .is-focused .checkbox-inline:focus, fieldset[disabled] .is-focused .checkbox-inline:hover, fieldset[disabled] .is-focused .checkbox label, fieldset[disabled] .is-focused .checkbox label:focus, fieldset[disabled] .is-focused .checkbox label:hover, fieldset[disabled] .is-focused .radio-inline, fieldset[disabled] .is-focused .radio-inline:focus, fieldset[disabled] .is-focused .radio-inline:hover, fieldset[disabled] .is-focused .radio label, fieldset[disabled] .is-focused .radio label:focus, fieldset[disabled] .is-focused .radio label:hover, fieldset[disabled] .is-focused .switch label, fieldset[disabled] .is-focused .switch label:focus, fieldset[disabled] .is-focused .switch label:hover, fieldset[disabled] .radio-inline, fieldset[disabled] .radio-inline:focus, fieldset[disabled] .radio-inline:hover, fieldset[disabled] .radio label, fieldset[disabled] .radio label:focus, fieldset[disabled] .radio label:hover, fieldset[disabled] .switch label, fieldset[disabled] .switch label:focus, fieldset[disabled] .switch label:hover { - color: rgba(0, 0, 0, .26) -} - -[class*=" bmd-label"], [class^=bmd-label] { - color: rgba(0, 0, 0, .26) -} - -.custom-file-control, .form-control, .is-focused .custom-file-control, .is-focused .form-control { - background-image: linear-gradient(0deg, #009688 2px, rgba(0, 150, 136, 0) 0), linear-gradient(0deg, rgba(0, 0, 0, .26) 1px, transparent 0) -} - -.custom-file-control:invalid, .form-control:invalid { - background-image: linear-gradient(0deg, #d50000 2px, rgba(213, 0, 0, 0) 0), linear-gradient(0deg, rgba(0, 0, 0, .26) 1px, transparent 0) -} - -.custom-file-control:read-only, .form-control:read-only { - background-image: linear-gradient(0deg, rgba(0, 0, 0, .26) 1px, transparent 0), linear-gradient(0deg, rgba(0, 0, 0, .26) 1px, transparent 0) -} - -.custom-file-control:disabled, .disabled.custom-file-control, .form-control.disabled, .form-control:disabled, .form-control[disabled], [disabled].custom-file-control, fieldset[disabled][disabled] .custom-file-control, fieldset[disabled][disabled] .form-control { - background-image: linear-gradient(90deg, rgba(0, 0, 0, .26) 0, rgba(0, 0, 0, .26) 30%, transparent 0, transparent); - background-repeat: repeat-x; - background-size: 3px 1px -} - -.form-control-success.custom-file-control, .form-control.form-control-success, .is-focused .form-control-success.custom-file-control, .is-focused .form-control.form-control-success { - background-image: linear-gradient(0deg, #009688 2px, rgba(0, 150, 136, 0) 0), linear-gradient(0deg, rgba(0, 0, 0, .26) 1px, transparent 0), "data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCA2MTIgNzkyIj48cGF0aCBmaWxsPSIjNWNiODVjIiBkPSJNMjMzLjggNjEwYy0xMy4zIDAtMjYtNi0zNC0xNi44TDkwLjUgNDQ4LjhDNzYuMyA0MzAgODAgNDAzLjMgOTguOCAzODljMTguOC0xNC4yIDQ1LjUtMTAuNCA1OS44IDguNGw3MiA5NUw0NTEuMyAyNDJjMTIuNS0yMCAzOC44LTI2LjIgNTguOC0xMy43IDIwIDEyLjQgMjYgMzguNyAxMy43IDU4LjhMMjcwIDU5MGMtNy40IDEyLTIwLjIgMTkuNC0zNC4zIDIwaC0yeiIvPjwvc3ZnPg==" -} - -.form-control-warning.custom-file-control, .form-control.form-control-warning, .is-focused .form-control-warning.custom-file-control, .is-focused .form-control.form-control-warning { - background-image: linear-gradient(0deg, #009688 2px, rgba(0, 150, 136, 0) 0), linear-gradient(0deg, rgba(0, 0, 0, .26) 1px, transparent 0), "data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCA2MTIgNzkyIj48cGF0aCBmaWxsPSIjZjBhZDRlIiBkPSJNNjAzIDY0MC4ybC0yNzguNS01MDljLTMuOC02LjYtMTAuOC0xMC42LTE4LjUtMTAuNnMtMTQuNyA0LTE4LjUgMTAuNkw5IDY0MC4yYy0zLjcgNi41LTMuNiAxNC40LjIgMjAuOCAzLjggNi41IDEwLjggMTAuNCAxOC4zIDEwLjRoNTU3YzcuNiAwIDE0LjYtNCAxOC40LTEwLjQgMy41LTYuNCAzLjYtMTQuNCAwLTIwLjh6bS0yNjYuNC0zMGgtNjEuMlY1NDloNjEuMnY2MS4yem0wLTEwN2gtNjEuMlYzMDRoNjEuMnYxOTl6Ii8+PC9zdmc+" -} - -.form-control-danger.custom-file-control, .form-control.form-control-danger, .is-focused .form-control-danger.custom-file-control, .is-focused .form-control.form-control-danger { - background-image: linear-gradient(0deg, #009688 2px, rgba(0, 150, 136, 0) 0), linear-gradient(0deg, rgba(0, 0, 0, .26) 1px, transparent 0), "data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCA2MTIgNzkyIj48cGF0aCBmaWxsPSIjZDk1MzRmIiBkPSJNNDQ3IDU0NC40Yy0xNC40IDE0LjQtMzcuNiAxNC40LTUyIDBsLTg5LTkyLjctODkgOTIuN2MtMTQuNSAxNC40LTM3LjcgMTQuNC01MiAwLTE0LjQtMTQuNC0xNC40LTM3LjYgMC01Mmw5Mi40LTk2LjMtOTIuNC05Ni4zYy0xNC40LTE0LjQtMTQuNC0zNy42IDAtNTJzMzcuNi0xNC4zIDUyIDBsODkgOTIuOCA4OS4yLTkyLjdjMTQuNC0xNC40IDM3LjYtMTQuNCA1MiAwIDE0LjMgMTQuNCAxNC4zIDM3LjYgMCA1MkwzNTQuNiAzOTZsOTIuNCA5Ni40YzE0LjQgMTQuNCAxNC40IDM3LjYgMCA1MnoiLz48L3N2Zz4=" -} - -.is-focused .valid-feedback { - display: none; - width: 100%; - margin-top: .25rem; - font-size: 80%; - color: rgba(0, 0, 0, .26) -} - -.is-focused .valid-tooltip { - position: absolute; - top: 100%; - z-index: 5; - display: none; - max-width: 100%; - padding: .5rem; - margin-top: .1rem; - font-size: .875rem; - line-height: 1; - color: #fff; - background-color: rgba(0, 0, 0, .8); - border-radius: .2rem -} - -.is-focused .custom-select.is-valid, .is-focused .form-control.is-valid, .is-focused .is-valid.custom-file-control, .was-validated .is-focused .custom-file-control:valid, .was-validated .is-focused .custom-select:valid, .was-validated .is-focused .form-control:valid { - border-color: rgba(0, 0, 0, .26) -} - -.is-focused .custom-select.is-valid:focus, .is-focused .form-control.is-valid:focus, .is-focused .is-valid.custom-file-control:focus, .was-validated .is-focused .custom-file-control:valid:focus, .was-validated .is-focused .custom-select:valid:focus, .was-validated .is-focused .form-control:valid:focus { - border-color: rgba(0, 0, 0, .26); - box-shadow: 0 0 0 .2rem rgba(0, 0, 0, .25) -} - -.is-focused .custom-select.is-valid ~ .valid-feedback, .is-focused .custom-select.is-valid ~ .valid-tooltip, .is-focused .form-control.is-valid ~ .valid-feedback, .is-focused .form-control.is-valid ~ .valid-tooltip, .is-focused .is-valid.custom-file-control ~ .valid-feedback, .is-focused .is-valid.custom-file-control ~ .valid-tooltip, .was-validated .is-focused .custom-file-control:valid ~ .valid-feedback, .was-validated .is-focused .custom-file-control:valid ~ .valid-tooltip, .was-validated .is-focused .custom-select:valid ~ .valid-feedback, .was-validated .is-focused .custom-select:valid ~ .valid-tooltip, .was-validated .is-focused .form-control:valid ~ .valid-feedback, .was-validated .is-focused .form-control:valid ~ .valid-tooltip { - display: block -} - -.is-focused .form-check-input.is-valid ~ .form-check-label, .was-validated .is-focused .form-check-input:valid ~ .form-check-label { - color: rgba(0, 0, 0, .26) -} - -.is-focused .form-check-input.is-valid ~ .valid-feedback, .is-focused .form-check-input.is-valid ~ .valid-tooltip, .was-validated .is-focused .form-check-input:valid ~ .valid-feedback, .was-validated .is-focused .form-check-input:valid ~ .valid-tooltip { - display: block -} - -.is-focused .custom-control-input.is-valid ~ .custom-control-label, .was-validated .is-focused .custom-control-input:valid ~ .custom-control-label { - color: rgba(0, 0, 0, .26) -} - -.is-focused .custom-control-input.is-valid ~ .custom-control-label:before, .was-validated .is-focused .custom-control-input:valid ~ .custom-control-label:before { - background-color: rgba(64, 64, 64, .26) -} - -.is-focused .custom-control-input.is-valid ~ .valid-feedback, .is-focused .custom-control-input.is-valid ~ .valid-tooltip, .was-validated .is-focused .custom-control-input:valid ~ .valid-feedback, .was-validated .is-focused .custom-control-input:valid ~ .valid-tooltip { - display: block -} - -.is-focused .custom-control-input.is-valid:checked ~ .custom-control-label:before, .was-validated .is-focused .custom-control-input:valid:checked ~ .custom-control-label:before { - background-color: rgba(26, 26, 26, .26) -} - -.is-focused .custom-control-input.is-valid:focus ~ .custom-control-label:before, .was-validated .is-focused .custom-control-input:valid:focus ~ .custom-control-label:before { - box-shadow: 0 0 0 1px #fafafa, 0 0 0 .2rem rgba(0, 0, 0, .25) -} - -.is-focused .custom-file-input.is-valid ~ .custom-file-label, .was-validated .is-focused .custom-file-input:valid ~ .custom-file-label { - border-color: rgba(0, 0, 0, .26) -} - -.is-focused .custom-file-input.is-valid ~ .custom-file-label:before, .was-validated .is-focused .custom-file-input:valid ~ .custom-file-label:before { - border-color: inherit -} - -.is-focused .custom-file-input.is-valid ~ .valid-feedback, .is-focused .custom-file-input.is-valid ~ .valid-tooltip, .was-validated .is-focused .custom-file-input:valid ~ .valid-feedback, .was-validated .is-focused .custom-file-input:valid ~ .valid-tooltip { - display: block -} - -.is-focused .custom-file-input.is-valid:focus ~ .custom-file-label, .was-validated .is-focused .custom-file-input:valid:focus ~ .custom-file-label { - box-shadow: 0 0 0 .2rem rgba(0, 0, 0, .25) -} - -.is-focused [class*=" bmd-label"], .is-focused [class^=bmd-label] { - color: #009688 -} - -.is-focused .bmd-label-placeholder { - color: rgba(0, 0, 0, .26) -} - -.is-focused .custom-file-control, .is-focused .form-control { - border-color: rgba(0, 0, 0, .26) -} - -.is-focused .bmd-help { - color: rgba(0, 0, 0, .54) -} - -.has-success [class*=" bmd-label"], .has-success [class^=bmd-label] { - color: #4caf50 -} - -.has-success .custom-file-control, .has-success .form-control, .is-focused .has-success .custom-file-control, .is-focused .has-success .form-control { - background-image: linear-gradient(0deg, #4caf50 2px, rgba(76, 175, 80, 0) 0), linear-gradient(0deg, rgba(0, 0, 0, .26) 1px, transparent 0) -} - -.has-success .custom-file-control:invalid, .has-success .form-control:invalid { - background-image: linear-gradient(0deg, #d50000 2px, rgba(213, 0, 0, 0) 0), linear-gradient(0deg, rgba(0, 0, 0, .26) 1px, transparent 0) -} - -.has-success .custom-file-control:read-only, .has-success .form-control:read-only { - background-image: linear-gradient(0deg, rgba(0, 0, 0, .26) 1px, transparent 0), linear-gradient(0deg, rgba(0, 0, 0, .26) 1px, transparent 0) -} - -.has-success .custom-file-control:disabled, .has-success .disabled.custom-file-control, .has-success .form-control.disabled, .has-success .form-control:disabled, .has-success .form-control[disabled], .has-success [disabled].custom-file-control, fieldset[disabled][disabled] .has-success .custom-file-control, fieldset[disabled][disabled] .has-success .form-control { - background-image: linear-gradient(90deg, rgba(0, 0, 0, .26) 0, rgba(0, 0, 0, .26) 30%, transparent 0, transparent); - background-repeat: repeat-x; - background-size: 3px 1px -} - -.has-success .form-control-success.custom-file-control, .has-success .form-control.form-control-success, .is-focused .has-success .form-control-success.custom-file-control, .is-focused .has-success .form-control.form-control-success { - background-image: linear-gradient(0deg, #4caf50 2px, rgba(76, 175, 80, 0) 0), linear-gradient(0deg, rgba(0, 0, 0, .26) 1px, transparent 0), "data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCA2MTIgNzkyIj48cGF0aCBmaWxsPSIjNWNiODVjIiBkPSJNMjMzLjggNjEwYy0xMy4zIDAtMjYtNi0zNC0xNi44TDkwLjUgNDQ4LjhDNzYuMyA0MzAgODAgNDAzLjMgOTguOCAzODljMTguOC0xNC4yIDQ1LjUtMTAuNCA1OS44IDguNGw3MiA5NUw0NTEuMyAyNDJjMTIuNS0yMCAzOC44LTI2LjIgNTguOC0xMy43IDIwIDEyLjQgMjYgMzguNyAxMy43IDU4LjhMMjcwIDU5MGMtNy40IDEyLTIwLjIgMTkuNC0zNC4zIDIwaC0yeiIvPjwvc3ZnPg==" -} - -.has-success .form-control-warning.custom-file-control, .has-success .form-control.form-control-warning, .is-focused .has-success .form-control-warning.custom-file-control, .is-focused .has-success .form-control.form-control-warning { - background-image: linear-gradient(0deg, #4caf50 2px, rgba(76, 175, 80, 0) 0), linear-gradient(0deg, rgba(0, 0, 0, .26) 1px, transparent 0), "data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCA2MTIgNzkyIj48cGF0aCBmaWxsPSIjZjBhZDRlIiBkPSJNNjAzIDY0MC4ybC0yNzguNS01MDljLTMuOC02LjYtMTAuOC0xMC42LTE4LjUtMTAuNnMtMTQuNyA0LTE4LjUgMTAuNkw5IDY0MC4yYy0zLjcgNi41LTMuNiAxNC40LjIgMjAuOCAzLjggNi41IDEwLjggMTAuNCAxOC4zIDEwLjRoNTU3YzcuNiAwIDE0LjYtNCAxOC40LTEwLjQgMy41LTYuNCAzLjYtMTQuNCAwLTIwLjh6bS0yNjYuNC0zMGgtNjEuMlY1NDloNjEuMnY2MS4yem0wLTEwN2gtNjEuMlYzMDRoNjEuMnYxOTl6Ii8+PC9zdmc+" -} - -.has-success .form-control-danger.custom-file-control, .has-success .form-control.form-control-danger, .is-focused .has-success .form-control-danger.custom-file-control, .is-focused .has-success .form-control.form-control-danger { - background-image: linear-gradient(0deg, #4caf50 2px, rgba(76, 175, 80, 0) 0), linear-gradient(0deg, rgba(0, 0, 0, .26) 1px, transparent 0), "data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCA2MTIgNzkyIj48cGF0aCBmaWxsPSIjZDk1MzRmIiBkPSJNNDQ3IDU0NC40Yy0xNC40IDE0LjQtMzcuNiAxNC40LTUyIDBsLTg5LTkyLjctODkgOTIuN2MtMTQuNSAxNC40LTM3LjcgMTQuNC01MiAwLTE0LjQtMTQuNC0xNC40LTM3LjYgMC01Mmw5Mi40LTk2LjMtOTIuNC05Ni4zYy0xNC40LTE0LjQtMTQuNC0zNy42IDAtNTJzMzcuNi0xNC4zIDUyIDBsODkgOTIuOCA4OS4yLTkyLjdjMTQuNC0xNC40IDM3LjYtMTQuNCA1MiAwIDE0LjMgMTQuNCAxNC4zIDM3LjYgMCA1MkwzNTQuNiAzOTZsOTIuNCA5Ni40YzE0LjQgMTQuNCAxNC40IDM3LjYgMCA1MnoiLz48L3N2Zz4=" -} - -.has-success.is-focused .valid-feedback, .has-success .is-focused .valid-feedback { - display: none; - width: 100%; - margin-top: .25rem; - font-size: 80%; - color: #4caf50 -} - -.has-success.is-focused .valid-tooltip, .has-success .is-focused .valid-tooltip { - position: absolute; - top: 100%; - z-index: 5; - display: none; - max-width: 100%; - padding: .5rem; - margin-top: .1rem; - font-size: .875rem; - line-height: 1; - color: #fff; - background-color: rgba(76, 175, 80, .8); - border-radius: .2rem -} - -.has-success.is-focused .custom-select.is-valid, .has-success .is-focused .custom-select.is-valid, .has-success.is-focused .form-control.is-valid, .has-success .is-focused .form-control.is-valid, .has-success.is-focused .is-valid.custom-file-control, .has-success .is-focused .is-valid.custom-file-control, .was-validated .has-success.is-focused .custom-file-control:valid, .was-validated .has-success .is-focused .custom-file-control:valid, .was-validated .has-success.is-focused .custom-select:valid, .was-validated .has-success .is-focused .custom-select:valid, .was-validated .has-success.is-focused .form-control:valid, .was-validated .has-success .is-focused .form-control:valid { - border-color: #4caf50 -} - -.has-success.is-focused .custom-select.is-valid:focus, .has-success .is-focused .custom-select.is-valid:focus, .has-success.is-focused .form-control.is-valid:focus, .has-success .is-focused .form-control.is-valid:focus, .has-success.is-focused .is-valid.custom-file-control:focus, .has-success .is-focused .is-valid.custom-file-control:focus, .was-validated .has-success.is-focused .custom-file-control:valid:focus, .was-validated .has-success .is-focused .custom-file-control:valid:focus, .was-validated .has-success.is-focused .custom-select:valid:focus, .was-validated .has-success .is-focused .custom-select:valid:focus, .was-validated .has-success.is-focused .form-control:valid:focus, .was-validated .has-success .is-focused .form-control:valid:focus { - border-color: #4caf50; - box-shadow: 0 0 0 .2rem rgba(76, 175, 80, .25) -} - -.has-success.is-focused .custom-select.is-valid ~ .valid-feedback, .has-success .is-focused .custom-select.is-valid ~ .valid-feedback, .has-success.is-focused .custom-select.is-valid ~ .valid-tooltip, .has-success .is-focused .custom-select.is-valid ~ .valid-tooltip, .has-success.is-focused .form-control.is-valid ~ .valid-feedback, .has-success .is-focused .form-control.is-valid ~ .valid-feedback, .has-success.is-focused .form-control.is-valid ~ .valid-tooltip, .has-success .is-focused .form-control.is-valid ~ .valid-tooltip, .has-success.is-focused .is-valid.custom-file-control ~ .valid-feedback, .has-success .is-focused .is-valid.custom-file-control ~ .valid-feedback, .has-success.is-focused .is-valid.custom-file-control ~ .valid-tooltip, .has-success .is-focused .is-valid.custom-file-control ~ .valid-tooltip, .was-validated .has-success.is-focused .custom-file-control:valid ~ .valid-feedback, .was-validated .has-success .is-focused .custom-file-control:valid ~ .valid-feedback, .was-validated .has-success.is-focused .custom-file-control:valid ~ .valid-tooltip, .was-validated .has-success .is-focused .custom-file-control:valid ~ .valid-tooltip, .was-validated .has-success.is-focused .custom-select:valid ~ .valid-feedback, .was-validated .has-success .is-focused .custom-select:valid ~ .valid-feedback, .was-validated .has-success.is-focused .custom-select:valid ~ .valid-tooltip, .was-validated .has-success .is-focused .custom-select:valid ~ .valid-tooltip, .was-validated .has-success.is-focused .form-control:valid ~ .valid-feedback, .was-validated .has-success .is-focused .form-control:valid ~ .valid-feedback, .was-validated .has-success.is-focused .form-control:valid ~ .valid-tooltip, .was-validated .has-success .is-focused .form-control:valid ~ .valid-tooltip { - display: block -} - -.has-success.is-focused .form-check-input.is-valid ~ .form-check-label, .has-success .is-focused .form-check-input.is-valid ~ .form-check-label, .was-validated .has-success.is-focused .form-check-input:valid ~ .form-check-label, .was-validated .has-success .is-focused .form-check-input:valid ~ .form-check-label { - color: #4caf50 -} - -.has-success.is-focused .form-check-input.is-valid ~ .valid-feedback, .has-success .is-focused .form-check-input.is-valid ~ .valid-feedback, .has-success.is-focused .form-check-input.is-valid ~ .valid-tooltip, .has-success .is-focused .form-check-input.is-valid ~ .valid-tooltip, .was-validated .has-success.is-focused .form-check-input:valid ~ .valid-feedback, .was-validated .has-success .is-focused .form-check-input:valid ~ .valid-feedback, .was-validated .has-success.is-focused .form-check-input:valid ~ .valid-tooltip, .was-validated .has-success .is-focused .form-check-input:valid ~ .valid-tooltip { - display: block -} - -.has-success.is-focused .custom-control-input.is-valid ~ .custom-control-label, .has-success .is-focused .custom-control-input.is-valid ~ .custom-control-label, .was-validated .has-success.is-focused .custom-control-input:valid ~ .custom-control-label, .was-validated .has-success .is-focused .custom-control-input:valid ~ .custom-control-label { - color: #4caf50 -} - -.has-success.is-focused .custom-control-input.is-valid ~ .custom-control-label:before, .has-success .is-focused .custom-control-input.is-valid ~ .custom-control-label:before, .was-validated .has-success.is-focused .custom-control-input:valid ~ .custom-control-label:before, .was-validated .has-success .is-focused .custom-control-input:valid ~ .custom-control-label:before { - background-color: #a3d7a5 -} - -.has-success.is-focused .custom-control-input.is-valid ~ .valid-feedback, .has-success .is-focused .custom-control-input.is-valid ~ .valid-feedback, .has-success.is-focused .custom-control-input.is-valid ~ .valid-tooltip, .has-success .is-focused .custom-control-input.is-valid ~ .valid-tooltip, .was-validated .has-success.is-focused .custom-control-input:valid ~ .valid-feedback, .was-validated .has-success .is-focused .custom-control-input:valid ~ .valid-feedback, .was-validated .has-success.is-focused .custom-control-input:valid ~ .valid-tooltip, .was-validated .has-success .is-focused .custom-control-input:valid ~ .valid-tooltip { - display: block -} - -.has-success.is-focused .custom-control-input.is-valid:checked ~ .custom-control-label:before, .has-success .is-focused .custom-control-input.is-valid:checked ~ .custom-control-label:before, .was-validated .has-success.is-focused .custom-control-input:valid:checked ~ .custom-control-label:before, .was-validated .has-success .is-focused .custom-control-input:valid:checked ~ .custom-control-label:before { - background-color: #6ec071 -} - -.has-success.is-focused .custom-control-input.is-valid:focus ~ .custom-control-label:before, .has-success .is-focused .custom-control-input.is-valid:focus ~ .custom-control-label:before, .was-validated .has-success.is-focused .custom-control-input:valid:focus ~ .custom-control-label:before, .was-validated .has-success .is-focused .custom-control-input:valid:focus ~ .custom-control-label:before { - box-shadow: 0 0 0 1px #fafafa, 0 0 0 .2rem rgba(76, 175, 80, .25) -} - -.has-success.is-focused .custom-file-input.is-valid ~ .custom-file-label, .has-success .is-focused .custom-file-input.is-valid ~ .custom-file-label, .was-validated .has-success.is-focused .custom-file-input:valid ~ .custom-file-label, .was-validated .has-success .is-focused .custom-file-input:valid ~ .custom-file-label { - border-color: #4caf50 -} - -.has-success.is-focused .custom-file-input.is-valid ~ .custom-file-label:before, .has-success .is-focused .custom-file-input.is-valid ~ .custom-file-label:before, .was-validated .has-success.is-focused .custom-file-input:valid ~ .custom-file-label:before, .was-validated .has-success .is-focused .custom-file-input:valid ~ .custom-file-label:before { - border-color: inherit -} - -.has-success.is-focused .custom-file-input.is-valid ~ .valid-feedback, .has-success .is-focused .custom-file-input.is-valid ~ .valid-feedback, .has-success.is-focused .custom-file-input.is-valid ~ .valid-tooltip, .has-success .is-focused .custom-file-input.is-valid ~ .valid-tooltip, .was-validated .has-success.is-focused .custom-file-input:valid ~ .valid-feedback, .was-validated .has-success .is-focused .custom-file-input:valid ~ .valid-feedback, .was-validated .has-success.is-focused .custom-file-input:valid ~ .valid-tooltip, .was-validated .has-success .is-focused .custom-file-input:valid ~ .valid-tooltip { - display: block -} - -.has-success.is-focused .custom-file-input.is-valid:focus ~ .custom-file-label, .has-success .is-focused .custom-file-input.is-valid:focus ~ .custom-file-label, .was-validated .has-success.is-focused .custom-file-input:valid:focus ~ .custom-file-label, .was-validated .has-success .is-focused .custom-file-input:valid:focus ~ .custom-file-label { - box-shadow: 0 0 0 .2rem rgba(76, 175, 80, .25) -} - -.has-success.is-focused .bmd-label-placeholder, .has-success .is-focused .bmd-label-placeholder, .has-success.is-focused [class*=" bmd-label"], .has-success .is-focused [class*=" bmd-label"], .has-success.is-focused [class^=bmd-label], .has-success .is-focused [class^=bmd-label] { - color: #4caf50 -} - -.has-success.is-focused .custom-file-control, .has-success .is-focused .custom-file-control, .has-success.is-focused .form-control, .has-success .is-focused .form-control { - border-color: #4caf50 -} - -.has-success.is-focused .bmd-help, .has-success .is-focused .bmd-help { - color: rgba(0, 0, 0, .54) -} - -.has-info [class*=" bmd-label"], .has-info [class^=bmd-label] { - color: #03a9f4 -} - -.has-info .custom-file-control, .has-info .form-control, .is-focused .has-info .custom-file-control, .is-focused .has-info .form-control { - background-image: linear-gradient(0deg, #03a9f4 2px, rgba(3, 169, 244, 0) 0), linear-gradient(0deg, rgba(0, 0, 0, .26) 1px, transparent 0) -} - -.has-info .custom-file-control:invalid, .has-info .form-control:invalid { - background-image: linear-gradient(0deg, #d50000 2px, rgba(213, 0, 0, 0) 0), linear-gradient(0deg, rgba(0, 0, 0, .26) 1px, transparent 0) -} - -.has-info .custom-file-control:read-only, .has-info .form-control:read-only { - background-image: linear-gradient(0deg, rgba(0, 0, 0, .26) 1px, transparent 0), linear-gradient(0deg, rgba(0, 0, 0, .26) 1px, transparent 0) -} - -.has-info .custom-file-control:disabled, .has-info .disabled.custom-file-control, .has-info .form-control.disabled, .has-info .form-control:disabled, .has-info .form-control[disabled], .has-info [disabled].custom-file-control, fieldset[disabled][disabled] .has-info .custom-file-control, fieldset[disabled][disabled] .has-info .form-control { - background-image: linear-gradient(90deg, rgba(0, 0, 0, .26) 0, rgba(0, 0, 0, .26) 30%, transparent 0, transparent); - background-repeat: repeat-x; - background-size: 3px 1px -} - -.has-info .form-control-success.custom-file-control, .has-info .form-control.form-control-success, .is-focused .has-info .form-control-success.custom-file-control, .is-focused .has-info .form-control.form-control-success { - background-image: linear-gradient(0deg, #03a9f4 2px, rgba(3, 169, 244, 0) 0), linear-gradient(0deg, rgba(0, 0, 0, .26) 1px, transparent 0), "data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCA2MTIgNzkyIj48cGF0aCBmaWxsPSIjNWNiODVjIiBkPSJNMjMzLjggNjEwYy0xMy4zIDAtMjYtNi0zNC0xNi44TDkwLjUgNDQ4LjhDNzYuMyA0MzAgODAgNDAzLjMgOTguOCAzODljMTguOC0xNC4yIDQ1LjUtMTAuNCA1OS44IDguNGw3MiA5NUw0NTEuMyAyNDJjMTIuNS0yMCAzOC44LTI2LjIgNTguOC0xMy43IDIwIDEyLjQgMjYgMzguNyAxMy43IDU4LjhMMjcwIDU5MGMtNy40IDEyLTIwLjIgMTkuNC0zNC4zIDIwaC0yeiIvPjwvc3ZnPg==" -} - -.has-info .form-control-warning.custom-file-control, .has-info .form-control.form-control-warning, .is-focused .has-info .form-control-warning.custom-file-control, .is-focused .has-info .form-control.form-control-warning { - background-image: linear-gradient(0deg, #03a9f4 2px, rgba(3, 169, 244, 0) 0), linear-gradient(0deg, rgba(0, 0, 0, .26) 1px, transparent 0), "data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCA2MTIgNzkyIj48cGF0aCBmaWxsPSIjZjBhZDRlIiBkPSJNNjAzIDY0MC4ybC0yNzguNS01MDljLTMuOC02LjYtMTAuOC0xMC42LTE4LjUtMTAuNnMtMTQuNyA0LTE4LjUgMTAuNkw5IDY0MC4yYy0zLjcgNi41LTMuNiAxNC40LjIgMjAuOCAzLjggNi41IDEwLjggMTAuNCAxOC4zIDEwLjRoNTU3YzcuNiAwIDE0LjYtNCAxOC40LTEwLjQgMy41LTYuNCAzLjYtMTQuNCAwLTIwLjh6bS0yNjYuNC0zMGgtNjEuMlY1NDloNjEuMnY2MS4yem0wLTEwN2gtNjEuMlYzMDRoNjEuMnYxOTl6Ii8+PC9zdmc+" -} - -.has-info .form-control-danger.custom-file-control, .has-info .form-control.form-control-danger, .is-focused .has-info .form-control-danger.custom-file-control, .is-focused .has-info .form-control.form-control-danger { - background-image: linear-gradient(0deg, #03a9f4 2px, rgba(3, 169, 244, 0) 0), linear-gradient(0deg, rgba(0, 0, 0, .26) 1px, transparent 0), "data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCA2MTIgNzkyIj48cGF0aCBmaWxsPSIjZDk1MzRmIiBkPSJNNDQ3IDU0NC40Yy0xNC40IDE0LjQtMzcuNiAxNC40LTUyIDBsLTg5LTkyLjctODkgOTIuN2MtMTQuNSAxNC40LTM3LjcgMTQuNC01MiAwLTE0LjQtMTQuNC0xNC40LTM3LjYgMC01Mmw5Mi40LTk2LjMtOTIuNC05Ni4zYy0xNC40LTE0LjQtMTQuNC0zNy42IDAtNTJzMzcuNi0xNC4zIDUyIDBsODkgOTIuOCA4OS4yLTkyLjdjMTQuNC0xNC40IDM3LjYtMTQuNCA1MiAwIDE0LjMgMTQuNCAxNC4zIDM3LjYgMCA1MkwzNTQuNiAzOTZsOTIuNCA5Ni40YzE0LjQgMTQuNCAxNC40IDM3LjYgMCA1MnoiLz48L3N2Zz4=" -} - -.has-info.is-focused .valid-feedback, .has-info .is-focused .valid-feedback { - display: none; - width: 100%; - margin-top: .25rem; - font-size: 80%; - color: #03a9f4 -} - -.has-info.is-focused .valid-tooltip, .has-info .is-focused .valid-tooltip { - position: absolute; - top: 100%; - z-index: 5; - display: none; - max-width: 100%; - padding: .5rem; - margin-top: .1rem; - font-size: .875rem; - line-height: 1; - color: #fff; - background-color: rgba(3, 169, 244, .8); - border-radius: .2rem -} - -.has-info.is-focused .custom-select.is-valid, .has-info .is-focused .custom-select.is-valid, .has-info.is-focused .form-control.is-valid, .has-info .is-focused .form-control.is-valid, .has-info.is-focused .is-valid.custom-file-control, .has-info .is-focused .is-valid.custom-file-control, .was-validated .has-info.is-focused .custom-file-control:valid, .was-validated .has-info .is-focused .custom-file-control:valid, .was-validated .has-info.is-focused .custom-select:valid, .was-validated .has-info .is-focused .custom-select:valid, .was-validated .has-info.is-focused .form-control:valid, .was-validated .has-info .is-focused .form-control:valid { - border-color: #03a9f4 -} - -.has-info.is-focused .custom-select.is-valid:focus, .has-info .is-focused .custom-select.is-valid:focus, .has-info.is-focused .form-control.is-valid:focus, .has-info .is-focused .form-control.is-valid:focus, .has-info.is-focused .is-valid.custom-file-control:focus, .has-info .is-focused .is-valid.custom-file-control:focus, .was-validated .has-info.is-focused .custom-file-control:valid:focus, .was-validated .has-info .is-focused .custom-file-control:valid:focus, .was-validated .has-info.is-focused .custom-select:valid:focus, .was-validated .has-info .is-focused .custom-select:valid:focus, .was-validated .has-info.is-focused .form-control:valid:focus, .was-validated .has-info .is-focused .form-control:valid:focus { - border-color: #03a9f4; - box-shadow: 0 0 0 .2rem rgba(3, 169, 244, .25) -} - -.has-info.is-focused .custom-select.is-valid ~ .valid-feedback, .has-info .is-focused .custom-select.is-valid ~ .valid-feedback, .has-info.is-focused .custom-select.is-valid ~ .valid-tooltip, .has-info .is-focused .custom-select.is-valid ~ .valid-tooltip, .has-info.is-focused .form-control.is-valid ~ .valid-feedback, .has-info .is-focused .form-control.is-valid ~ .valid-feedback, .has-info.is-focused .form-control.is-valid ~ .valid-tooltip, .has-info .is-focused .form-control.is-valid ~ .valid-tooltip, .has-info.is-focused .is-valid.custom-file-control ~ .valid-feedback, .has-info .is-focused .is-valid.custom-file-control ~ .valid-feedback, .has-info.is-focused .is-valid.custom-file-control ~ .valid-tooltip, .has-info .is-focused .is-valid.custom-file-control ~ .valid-tooltip, .was-validated .has-info.is-focused .custom-file-control:valid ~ .valid-feedback, .was-validated .has-info .is-focused .custom-file-control:valid ~ .valid-feedback, .was-validated .has-info.is-focused .custom-file-control:valid ~ .valid-tooltip, .was-validated .has-info .is-focused .custom-file-control:valid ~ .valid-tooltip, .was-validated .has-info.is-focused .custom-select:valid ~ .valid-feedback, .was-validated .has-info .is-focused .custom-select:valid ~ .valid-feedback, .was-validated .has-info.is-focused .custom-select:valid ~ .valid-tooltip, .was-validated .has-info .is-focused .custom-select:valid ~ .valid-tooltip, .was-validated .has-info.is-focused .form-control:valid ~ .valid-feedback, .was-validated .has-info .is-focused .form-control:valid ~ .valid-feedback, .was-validated .has-info.is-focused .form-control:valid ~ .valid-tooltip, .was-validated .has-info .is-focused .form-control:valid ~ .valid-tooltip { - display: block -} - -.has-info.is-focused .form-check-input.is-valid ~ .form-check-label, .has-info .is-focused .form-check-input.is-valid ~ .form-check-label, .was-validated .has-info.is-focused .form-check-input:valid ~ .form-check-label, .was-validated .has-info .is-focused .form-check-input:valid ~ .form-check-label { - color: #03a9f4 -} - -.has-info.is-focused .form-check-input.is-valid ~ .valid-feedback, .has-info .is-focused .form-check-input.is-valid ~ .valid-feedback, .has-info.is-focused .form-check-input.is-valid ~ .valid-tooltip, .has-info .is-focused .form-check-input.is-valid ~ .valid-tooltip, .was-validated .has-info.is-focused .form-check-input:valid ~ .valid-feedback, .was-validated .has-info .is-focused .form-check-input:valid ~ .valid-feedback, .was-validated .has-info.is-focused .form-check-input:valid ~ .valid-tooltip, .was-validated .has-info .is-focused .form-check-input:valid ~ .valid-tooltip { - display: block -} - -.has-info.is-focused .custom-control-input.is-valid ~ .custom-control-label, .has-info .is-focused .custom-control-input.is-valid ~ .custom-control-label, .was-validated .has-info.is-focused .custom-control-input:valid ~ .custom-control-label, .was-validated .has-info .is-focused .custom-control-input:valid ~ .custom-control-label { - color: #03a9f4 -} - -.has-info.is-focused .custom-control-input.is-valid ~ .custom-control-label:before, .has-info .is-focused .custom-control-input.is-valid ~ .custom-control-label:before, .was-validated .has-info.is-focused .custom-control-input:valid ~ .custom-control-label:before, .was-validated .has-info .is-focused .custom-control-input:valid ~ .custom-control-label:before { - background-color: #79d4fd -} - -.has-info.is-focused .custom-control-input.is-valid ~ .valid-feedback, .has-info .is-focused .custom-control-input.is-valid ~ .valid-feedback, .has-info.is-focused .custom-control-input.is-valid ~ .valid-tooltip, .has-info .is-focused .custom-control-input.is-valid ~ .valid-tooltip, .was-validated .has-info.is-focused .custom-control-input:valid ~ .valid-feedback, .was-validated .has-info .is-focused .custom-control-input:valid ~ .valid-feedback, .was-validated .has-info.is-focused .custom-control-input:valid ~ .valid-tooltip, .was-validated .has-info .is-focused .custom-control-input:valid ~ .valid-tooltip { - display: block -} - -.has-info.is-focused .custom-control-input.is-valid:checked ~ .custom-control-label:before, .has-info .is-focused .custom-control-input.is-valid:checked ~ .custom-control-label:before, .was-validated .has-info.is-focused .custom-control-input:valid:checked ~ .custom-control-label:before, .was-validated .has-info .is-focused .custom-control-input:valid:checked ~ .custom-control-label:before { - background-color: #2ebcfc -} - -.has-info.is-focused .custom-control-input.is-valid:focus ~ .custom-control-label:before, .has-info .is-focused .custom-control-input.is-valid:focus ~ .custom-control-label:before, .was-validated .has-info.is-focused .custom-control-input:valid:focus ~ .custom-control-label:before, .was-validated .has-info .is-focused .custom-control-input:valid:focus ~ .custom-control-label:before { - box-shadow: 0 0 0 1px #fafafa, 0 0 0 .2rem rgba(3, 169, 244, .25) -} - -.has-info.is-focused .custom-file-input.is-valid ~ .custom-file-label, .has-info .is-focused .custom-file-input.is-valid ~ .custom-file-label, .was-validated .has-info.is-focused .custom-file-input:valid ~ .custom-file-label, .was-validated .has-info .is-focused .custom-file-input:valid ~ .custom-file-label { - border-color: #03a9f4 -} - -.has-info.is-focused .custom-file-input.is-valid ~ .custom-file-label:before, .has-info .is-focused .custom-file-input.is-valid ~ .custom-file-label:before, .was-validated .has-info.is-focused .custom-file-input:valid ~ .custom-file-label:before, .was-validated .has-info .is-focused .custom-file-input:valid ~ .custom-file-label:before { - border-color: inherit -} - -.has-info.is-focused .custom-file-input.is-valid ~ .valid-feedback, .has-info .is-focused .custom-file-input.is-valid ~ .valid-feedback, .has-info.is-focused .custom-file-input.is-valid ~ .valid-tooltip, .has-info .is-focused .custom-file-input.is-valid ~ .valid-tooltip, .was-validated .has-info.is-focused .custom-file-input:valid ~ .valid-feedback, .was-validated .has-info .is-focused .custom-file-input:valid ~ .valid-feedback, .was-validated .has-info.is-focused .custom-file-input:valid ~ .valid-tooltip, .was-validated .has-info .is-focused .custom-file-input:valid ~ .valid-tooltip { - display: block -} - -.has-info.is-focused .custom-file-input.is-valid:focus ~ .custom-file-label, .has-info .is-focused .custom-file-input.is-valid:focus ~ .custom-file-label, .was-validated .has-info.is-focused .custom-file-input:valid:focus ~ .custom-file-label, .was-validated .has-info .is-focused .custom-file-input:valid:focus ~ .custom-file-label { - box-shadow: 0 0 0 .2rem rgba(3, 169, 244, .25) -} - -.has-info.is-focused .bmd-label-placeholder, .has-info .is-focused .bmd-label-placeholder, .has-info.is-focused [class*=" bmd-label"], .has-info .is-focused [class*=" bmd-label"], .has-info.is-focused [class^=bmd-label], .has-info .is-focused [class^=bmd-label] { - color: #03a9f4 -} - -.has-info.is-focused .custom-file-control, .has-info .is-focused .custom-file-control, .has-info.is-focused .form-control, .has-info .is-focused .form-control { - border-color: #03a9f4 -} - -.has-info.is-focused .bmd-help, .has-info .is-focused .bmd-help { - color: rgba(0, 0, 0, .54) -} - -.has-warning [class*=" bmd-label"], .has-warning [class^=bmd-label] { - color: #ff5722 -} - -.has-warning .custom-file-control, .has-warning .form-control, .is-focused .has-warning .custom-file-control, .is-focused .has-warning .form-control { - background-image: linear-gradient(0deg, #ff5722 2px, rgba(255, 87, 34, 0) 0), linear-gradient(0deg, rgba(0, 0, 0, .26) 1px, transparent 0) -} - -.has-warning .custom-file-control:invalid, .has-warning .form-control:invalid { - background-image: linear-gradient(0deg, #d50000 2px, rgba(213, 0, 0, 0) 0), linear-gradient(0deg, rgba(0, 0, 0, .26) 1px, transparent 0) -} - -.has-warning .custom-file-control:read-only, .has-warning .form-control:read-only { - background-image: linear-gradient(0deg, rgba(0, 0, 0, .26) 1px, transparent 0), linear-gradient(0deg, rgba(0, 0, 0, .26) 1px, transparent 0) -} - -.has-warning .custom-file-control:disabled, .has-warning .disabled.custom-file-control, .has-warning .form-control.disabled, .has-warning .form-control:disabled, .has-warning .form-control[disabled], .has-warning [disabled].custom-file-control, fieldset[disabled][disabled] .has-warning .custom-file-control, fieldset[disabled][disabled] .has-warning .form-control { - background-image: linear-gradient(90deg, rgba(0, 0, 0, .26) 0, rgba(0, 0, 0, .26) 30%, transparent 0, transparent); - background-repeat: repeat-x; - background-size: 3px 1px -} - -.has-warning .form-control-success.custom-file-control, .has-warning .form-control.form-control-success, .is-focused .has-warning .form-control-success.custom-file-control, .is-focused .has-warning .form-control.form-control-success { - background-image: linear-gradient(0deg, #ff5722 2px, rgba(255, 87, 34, 0) 0), linear-gradient(0deg, rgba(0, 0, 0, .26) 1px, transparent 0), "data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCA2MTIgNzkyIj48cGF0aCBmaWxsPSIjNWNiODVjIiBkPSJNMjMzLjggNjEwYy0xMy4zIDAtMjYtNi0zNC0xNi44TDkwLjUgNDQ4LjhDNzYuMyA0MzAgODAgNDAzLjMgOTguOCAzODljMTguOC0xNC4yIDQ1LjUtMTAuNCA1OS44IDguNGw3MiA5NUw0NTEuMyAyNDJjMTIuNS0yMCAzOC44LTI2LjIgNTguOC0xMy43IDIwIDEyLjQgMjYgMzguNyAxMy43IDU4LjhMMjcwIDU5MGMtNy40IDEyLTIwLjIgMTkuNC0zNC4zIDIwaC0yeiIvPjwvc3ZnPg==" -} - -.has-warning .form-control-warning.custom-file-control, .has-warning .form-control.form-control-warning, .is-focused .has-warning .form-control-warning.custom-file-control, .is-focused .has-warning .form-control.form-control-warning { - background-image: linear-gradient(0deg, #ff5722 2px, rgba(255, 87, 34, 0) 0), linear-gradient(0deg, rgba(0, 0, 0, .26) 1px, transparent 0), "data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCA2MTIgNzkyIj48cGF0aCBmaWxsPSIjZjBhZDRlIiBkPSJNNjAzIDY0MC4ybC0yNzguNS01MDljLTMuOC02LjYtMTAuOC0xMC42LTE4LjUtMTAuNnMtMTQuNyA0LTE4LjUgMTAuNkw5IDY0MC4yYy0zLjcgNi41LTMuNiAxNC40LjIgMjAuOCAzLjggNi41IDEwLjggMTAuNCAxOC4zIDEwLjRoNTU3YzcuNiAwIDE0LjYtNCAxOC40LTEwLjQgMy41LTYuNCAzLjYtMTQuNCAwLTIwLjh6bS0yNjYuNC0zMGgtNjEuMlY1NDloNjEuMnY2MS4yem0wLTEwN2gtNjEuMlYzMDRoNjEuMnYxOTl6Ii8+PC9zdmc+" -} - -.has-warning .form-control-danger.custom-file-control, .has-warning .form-control.form-control-danger, .is-focused .has-warning .form-control-danger.custom-file-control, .is-focused .has-warning .form-control.form-control-danger { - background-image: linear-gradient(0deg, #ff5722 2px, rgba(255, 87, 34, 0) 0), linear-gradient(0deg, rgba(0, 0, 0, .26) 1px, transparent 0), "data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCA2MTIgNzkyIj48cGF0aCBmaWxsPSIjZDk1MzRmIiBkPSJNNDQ3IDU0NC40Yy0xNC40IDE0LjQtMzcuNiAxNC40LTUyIDBsLTg5LTkyLjctODkgOTIuN2MtMTQuNSAxNC40LTM3LjcgMTQuNC01MiAwLTE0LjQtMTQuNC0xNC40LTM3LjYgMC01Mmw5Mi40LTk2LjMtOTIuNC05Ni4zYy0xNC40LTE0LjQtMTQuNC0zNy42IDAtNTJzMzcuNi0xNC4zIDUyIDBsODkgOTIuOCA4OS4yLTkyLjdjMTQuNC0xNC40IDM3LjYtMTQuNCA1MiAwIDE0LjMgMTQuNCAxNC4zIDM3LjYgMCA1MkwzNTQuNiAzOTZsOTIuNCA5Ni40YzE0LjQgMTQuNCAxNC40IDM3LjYgMCA1MnoiLz48L3N2Zz4=" -} - -.has-warning.is-focused .valid-feedback, .has-warning .is-focused .valid-feedback { - display: none; - width: 100%; - margin-top: .25rem; - font-size: 80%; - color: #ff5722 -} - -.has-warning.is-focused .valid-tooltip, .has-warning .is-focused .valid-tooltip { - position: absolute; - top: 100%; - z-index: 5; - display: none; - max-width: 100%; - padding: .5rem; - margin-top: .1rem; - font-size: .875rem; - line-height: 1; - color: #fff; - background-color: rgba(255, 87, 34, .8); - border-radius: .2rem -} - -.has-warning.is-focused .custom-select.is-valid, .has-warning .is-focused .custom-select.is-valid, .has-warning.is-focused .form-control.is-valid, .has-warning .is-focused .form-control.is-valid, .has-warning.is-focused .is-valid.custom-file-control, .has-warning .is-focused .is-valid.custom-file-control, .was-validated .has-warning.is-focused .custom-file-control:valid, .was-validated .has-warning .is-focused .custom-file-control:valid, .was-validated .has-warning.is-focused .custom-select:valid, .was-validated .has-warning .is-focused .custom-select:valid, .was-validated .has-warning.is-focused .form-control:valid, .was-validated .has-warning .is-focused .form-control:valid { - border-color: #ff5722 -} - -.has-warning.is-focused .custom-select.is-valid:focus, .has-warning .is-focused .custom-select.is-valid:focus, .has-warning.is-focused .form-control.is-valid:focus, .has-warning .is-focused .form-control.is-valid:focus, .has-warning.is-focused .is-valid.custom-file-control:focus, .has-warning .is-focused .is-valid.custom-file-control:focus, .was-validated .has-warning.is-focused .custom-file-control:valid:focus, .was-validated .has-warning .is-focused .custom-file-control:valid:focus, .was-validated .has-warning.is-focused .custom-select:valid:focus, .was-validated .has-warning .is-focused .custom-select:valid:focus, .was-validated .has-warning.is-focused .form-control:valid:focus, .was-validated .has-warning .is-focused .form-control:valid:focus { - border-color: #ff5722; - box-shadow: 0 0 0 .2rem rgba(255, 87, 34, .25) -} - -.has-warning.is-focused .custom-select.is-valid ~ .valid-feedback, .has-warning .is-focused .custom-select.is-valid ~ .valid-feedback, .has-warning.is-focused .custom-select.is-valid ~ .valid-tooltip, .has-warning .is-focused .custom-select.is-valid ~ .valid-tooltip, .has-warning.is-focused .form-control.is-valid ~ .valid-feedback, .has-warning .is-focused .form-control.is-valid ~ .valid-feedback, .has-warning.is-focused .form-control.is-valid ~ .valid-tooltip, .has-warning .is-focused .form-control.is-valid ~ .valid-tooltip, .has-warning.is-focused .is-valid.custom-file-control ~ .valid-feedback, .has-warning .is-focused .is-valid.custom-file-control ~ .valid-feedback, .has-warning.is-focused .is-valid.custom-file-control ~ .valid-tooltip, .has-warning .is-focused .is-valid.custom-file-control ~ .valid-tooltip, .was-validated .has-warning.is-focused .custom-file-control:valid ~ .valid-feedback, .was-validated .has-warning .is-focused .custom-file-control:valid ~ .valid-feedback, .was-validated .has-warning.is-focused .custom-file-control:valid ~ .valid-tooltip, .was-validated .has-warning .is-focused .custom-file-control:valid ~ .valid-tooltip, .was-validated .has-warning.is-focused .custom-select:valid ~ .valid-feedback, .was-validated .has-warning .is-focused .custom-select:valid ~ .valid-feedback, .was-validated .has-warning.is-focused .custom-select:valid ~ .valid-tooltip, .was-validated .has-warning .is-focused .custom-select:valid ~ .valid-tooltip, .was-validated .has-warning.is-focused .form-control:valid ~ .valid-feedback, .was-validated .has-warning .is-focused .form-control:valid ~ .valid-feedback, .was-validated .has-warning.is-focused .form-control:valid ~ .valid-tooltip, .was-validated .has-warning .is-focused .form-control:valid ~ .valid-tooltip { - display: block -} - -.has-warning.is-focused .form-check-input.is-valid ~ .form-check-label, .has-warning .is-focused .form-check-input.is-valid ~ .form-check-label, .was-validated .has-warning.is-focused .form-check-input:valid ~ .form-check-label, .was-validated .has-warning .is-focused .form-check-input:valid ~ .form-check-label { - color: #ff5722 -} - -.has-warning.is-focused .form-check-input.is-valid ~ .valid-feedback, .has-warning .is-focused .form-check-input.is-valid ~ .valid-feedback, .has-warning.is-focused .form-check-input.is-valid ~ .valid-tooltip, .has-warning .is-focused .form-check-input.is-valid ~ .valid-tooltip, .was-validated .has-warning.is-focused .form-check-input:valid ~ .valid-feedback, .was-validated .has-warning .is-focused .form-check-input:valid ~ .valid-feedback, .was-validated .has-warning.is-focused .form-check-input:valid ~ .valid-tooltip, .was-validated .has-warning .is-focused .form-check-input:valid ~ .valid-tooltip { - display: block -} - -.has-warning.is-focused .custom-control-input.is-valid ~ .custom-control-label, .has-warning .is-focused .custom-control-input.is-valid ~ .custom-control-label, .was-validated .has-warning.is-focused .custom-control-input:valid ~ .custom-control-label, .was-validated .has-warning .is-focused .custom-control-input:valid ~ .custom-control-label { - color: #ff5722 -} - -.has-warning.is-focused .custom-control-input.is-valid ~ .custom-control-label:before, .has-warning .is-focused .custom-control-input.is-valid ~ .custom-control-label:before, .was-validated .has-warning.is-focused .custom-control-input:valid ~ .custom-control-label:before, .was-validated .has-warning .is-focused .custom-control-input:valid ~ .custom-control-label:before { - background-color: #ffb8a2 -} - -.has-warning.is-focused .custom-control-input.is-valid ~ .valid-feedback, .has-warning .is-focused .custom-control-input.is-valid ~ .valid-feedback, .has-warning.is-focused .custom-control-input.is-valid ~ .valid-tooltip, .has-warning .is-focused .custom-control-input.is-valid ~ .valid-tooltip, .was-validated .has-warning.is-focused .custom-control-input:valid ~ .valid-feedback, .was-validated .has-warning .is-focused .custom-control-input:valid ~ .valid-feedback, .was-validated .has-warning.is-focused .custom-control-input:valid ~ .valid-tooltip, .was-validated .has-warning .is-focused .custom-control-input:valid ~ .valid-tooltip { - display: block -} - -.has-warning.is-focused .custom-control-input.is-valid:checked ~ .custom-control-label:before, .has-warning .is-focused .custom-control-input.is-valid:checked ~ .custom-control-label:before, .was-validated .has-warning.is-focused .custom-control-input:valid:checked ~ .custom-control-label:before, .was-validated .has-warning .is-focused .custom-control-input:valid:checked ~ .custom-control-label:before { - background-color: #ff7e55 -} - -.has-warning.is-focused .custom-control-input.is-valid:focus ~ .custom-control-label:before, .has-warning .is-focused .custom-control-input.is-valid:focus ~ .custom-control-label:before, .was-validated .has-warning.is-focused .custom-control-input:valid:focus ~ .custom-control-label:before, .was-validated .has-warning .is-focused .custom-control-input:valid:focus ~ .custom-control-label:before { - box-shadow: 0 0 0 1px #fafafa, 0 0 0 .2rem rgba(255, 87, 34, .25) -} - -.has-warning.is-focused .custom-file-input.is-valid ~ .custom-file-label, .has-warning .is-focused .custom-file-input.is-valid ~ .custom-file-label, .was-validated .has-warning.is-focused .custom-file-input:valid ~ .custom-file-label, .was-validated .has-warning .is-focused .custom-file-input:valid ~ .custom-file-label { - border-color: #ff5722 -} - -.has-warning.is-focused .custom-file-input.is-valid ~ .custom-file-label:before, .has-warning .is-focused .custom-file-input.is-valid ~ .custom-file-label:before, .was-validated .has-warning.is-focused .custom-file-input:valid ~ .custom-file-label:before, .was-validated .has-warning .is-focused .custom-file-input:valid ~ .custom-file-label:before { - border-color: inherit -} - -.has-warning.is-focused .custom-file-input.is-valid ~ .valid-feedback, .has-warning .is-focused .custom-file-input.is-valid ~ .valid-feedback, .has-warning.is-focused .custom-file-input.is-valid ~ .valid-tooltip, .has-warning .is-focused .custom-file-input.is-valid ~ .valid-tooltip, .was-validated .has-warning.is-focused .custom-file-input:valid ~ .valid-feedback, .was-validated .has-warning .is-focused .custom-file-input:valid ~ .valid-feedback, .was-validated .has-warning.is-focused .custom-file-input:valid ~ .valid-tooltip, .was-validated .has-warning .is-focused .custom-file-input:valid ~ .valid-tooltip { - display: block -} - -.has-warning.is-focused .custom-file-input.is-valid:focus ~ .custom-file-label, .has-warning .is-focused .custom-file-input.is-valid:focus ~ .custom-file-label, .was-validated .has-warning.is-focused .custom-file-input:valid:focus ~ .custom-file-label, .was-validated .has-warning .is-focused .custom-file-input:valid:focus ~ .custom-file-label { - box-shadow: 0 0 0 .2rem rgba(255, 87, 34, .25) -} - -.has-warning.is-focused .bmd-label-placeholder, .has-warning .is-focused .bmd-label-placeholder, .has-warning.is-focused [class*=" bmd-label"], .has-warning .is-focused [class*=" bmd-label"], .has-warning.is-focused [class^=bmd-label], .has-warning .is-focused [class^=bmd-label] { - color: #ff5722 -} - -.has-warning.is-focused .custom-file-control, .has-warning .is-focused .custom-file-control, .has-warning.is-focused .form-control, .has-warning .is-focused .form-control { - border-color: #ff5722 -} - -.has-warning.is-focused .bmd-help, .has-warning .is-focused .bmd-help { - color: rgba(0, 0, 0, .54) -} - -.has-danger [class*=" bmd-label"], .has-danger [class^=bmd-label] { - color: #f44336 -} - -.has-danger .custom-file-control, .has-danger .form-control, .is-focused .has-danger .custom-file-control, .is-focused .has-danger .form-control { - background-image: linear-gradient(0deg, #f44336 2px, rgba(244, 67, 54, 0) 0), linear-gradient(0deg, rgba(0, 0, 0, .26) 1px, transparent 0) -} - -.has-danger .custom-file-control:invalid, .has-danger .form-control:invalid { - background-image: linear-gradient(0deg, #d50000 2px, rgba(213, 0, 0, 0) 0), linear-gradient(0deg, rgba(0, 0, 0, .26) 1px, transparent 0) -} - -.has-danger .custom-file-control:read-only, .has-danger .form-control:read-only { - background-image: linear-gradient(0deg, rgba(0, 0, 0, .26) 1px, transparent 0), linear-gradient(0deg, rgba(0, 0, 0, .26) 1px, transparent 0) -} - -.has-danger .custom-file-control:disabled, .has-danger .disabled.custom-file-control, .has-danger .form-control.disabled, .has-danger .form-control:disabled, .has-danger .form-control[disabled], .has-danger [disabled].custom-file-control, fieldset[disabled][disabled] .has-danger .custom-file-control, fieldset[disabled][disabled] .has-danger .form-control { - background-image: linear-gradient(90deg, rgba(0, 0, 0, .26) 0, rgba(0, 0, 0, .26) 30%, transparent 0, transparent); - background-repeat: repeat-x; - background-size: 3px 1px -} - -.has-danger .form-control-success.custom-file-control, .has-danger .form-control.form-control-success, .is-focused .has-danger .form-control-success.custom-file-control, .is-focused .has-danger .form-control.form-control-success { - background-image: linear-gradient(0deg, #f44336 2px, rgba(244, 67, 54, 0) 0), linear-gradient(0deg, rgba(0, 0, 0, .26) 1px, transparent 0), "data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCA2MTIgNzkyIj48cGF0aCBmaWxsPSIjNWNiODVjIiBkPSJNMjMzLjggNjEwYy0xMy4zIDAtMjYtNi0zNC0xNi44TDkwLjUgNDQ4LjhDNzYuMyA0MzAgODAgNDAzLjMgOTguOCAzODljMTguOC0xNC4yIDQ1LjUtMTAuNCA1OS44IDguNGw3MiA5NUw0NTEuMyAyNDJjMTIuNS0yMCAzOC44LTI2LjIgNTguOC0xMy43IDIwIDEyLjQgMjYgMzguNyAxMy43IDU4LjhMMjcwIDU5MGMtNy40IDEyLTIwLjIgMTkuNC0zNC4zIDIwaC0yeiIvPjwvc3ZnPg==" -} - -.has-danger .form-control-warning.custom-file-control, .has-danger .form-control.form-control-warning, .is-focused .has-danger .form-control-warning.custom-file-control, .is-focused .has-danger .form-control.form-control-warning { - background-image: linear-gradient(0deg, #f44336 2px, rgba(244, 67, 54, 0) 0), linear-gradient(0deg, rgba(0, 0, 0, .26) 1px, transparent 0), "data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCA2MTIgNzkyIj48cGF0aCBmaWxsPSIjZjBhZDRlIiBkPSJNNjAzIDY0MC4ybC0yNzguNS01MDljLTMuOC02LjYtMTAuOC0xMC42LTE4LjUtMTAuNnMtMTQuNyA0LTE4LjUgMTAuNkw5IDY0MC4yYy0zLjcgNi41LTMuNiAxNC40LjIgMjAuOCAzLjggNi41IDEwLjggMTAuNCAxOC4zIDEwLjRoNTU3YzcuNiAwIDE0LjYtNCAxOC40LTEwLjQgMy41LTYuNCAzLjYtMTQuNCAwLTIwLjh6bS0yNjYuNC0zMGgtNjEuMlY1NDloNjEuMnY2MS4yem0wLTEwN2gtNjEuMlYzMDRoNjEuMnYxOTl6Ii8+PC9zdmc+" -} - -.has-danger .form-control-danger.custom-file-control, .has-danger .form-control.form-control-danger, .is-focused .has-danger .form-control-danger.custom-file-control, .is-focused .has-danger .form-control.form-control-danger { - background-image: linear-gradient(0deg, #f44336 2px, rgba(244, 67, 54, 0) 0), linear-gradient(0deg, rgba(0, 0, 0, .26) 1px, transparent 0), "data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCA2MTIgNzkyIj48cGF0aCBmaWxsPSIjZDk1MzRmIiBkPSJNNDQ3IDU0NC40Yy0xNC40IDE0LjQtMzcuNiAxNC40LTUyIDBsLTg5LTkyLjctODkgOTIuN2MtMTQuNSAxNC40LTM3LjcgMTQuNC01MiAwLTE0LjQtMTQuNC0xNC40LTM3LjYgMC01Mmw5Mi40LTk2LjMtOTIuNC05Ni4zYy0xNC40LTE0LjQtMTQuNC0zNy42IDAtNTJzMzcuNi0xNC4zIDUyIDBsODkgOTIuOCA4OS4yLTkyLjdjMTQuNC0xNC40IDM3LjYtMTQuNCA1MiAwIDE0LjMgMTQuNCAxNC4zIDM3LjYgMCA1MkwzNTQuNiAzOTZsOTIuNCA5Ni40YzE0LjQgMTQuNCAxNC40IDM3LjYgMCA1MnoiLz48L3N2Zz4=" -} - -.has-danger.is-focused .valid-feedback, .has-danger .is-focused .valid-feedback { - display: none; - width: 100%; - margin-top: .25rem; - font-size: 80%; - color: #f44336 -} - -.has-danger.is-focused .valid-tooltip, .has-danger .is-focused .valid-tooltip { - position: absolute; - top: 100%; - z-index: 5; - display: none; - max-width: 100%; - padding: .5rem; - margin-top: .1rem; - font-size: .875rem; - line-height: 1; - color: #fff; - background-color: rgba(244, 67, 54, .8); - border-radius: .2rem -} - -.has-danger.is-focused .custom-select.is-valid, .has-danger .is-focused .custom-select.is-valid, .has-danger.is-focused .form-control.is-valid, .has-danger .is-focused .form-control.is-valid, .has-danger.is-focused .is-valid.custom-file-control, .has-danger .is-focused .is-valid.custom-file-control, .was-validated .has-danger.is-focused .custom-file-control:valid, .was-validated .has-danger .is-focused .custom-file-control:valid, .was-validated .has-danger.is-focused .custom-select:valid, .was-validated .has-danger .is-focused .custom-select:valid, .was-validated .has-danger.is-focused .form-control:valid, .was-validated .has-danger .is-focused .form-control:valid { - border-color: #f44336 -} - -.has-danger.is-focused .custom-select.is-valid:focus, .has-danger .is-focused .custom-select.is-valid:focus, .has-danger.is-focused .form-control.is-valid:focus, .has-danger .is-focused .form-control.is-valid:focus, .has-danger.is-focused .is-valid.custom-file-control:focus, .has-danger .is-focused .is-valid.custom-file-control:focus, .was-validated .has-danger.is-focused .custom-file-control:valid:focus, .was-validated .has-danger .is-focused .custom-file-control:valid:focus, .was-validated .has-danger.is-focused .custom-select:valid:focus, .was-validated .has-danger .is-focused .custom-select:valid:focus, .was-validated .has-danger.is-focused .form-control:valid:focus, .was-validated .has-danger .is-focused .form-control:valid:focus { - border-color: #f44336; - box-shadow: 0 0 0 .2rem rgba(244, 67, 54, .25) -} - -.has-danger.is-focused .custom-select.is-valid ~ .valid-feedback, .has-danger .is-focused .custom-select.is-valid ~ .valid-feedback, .has-danger.is-focused .custom-select.is-valid ~ .valid-tooltip, .has-danger .is-focused .custom-select.is-valid ~ .valid-tooltip, .has-danger.is-focused .form-control.is-valid ~ .valid-feedback, .has-danger .is-focused .form-control.is-valid ~ .valid-feedback, .has-danger.is-focused .form-control.is-valid ~ .valid-tooltip, .has-danger .is-focused .form-control.is-valid ~ .valid-tooltip, .has-danger.is-focused .is-valid.custom-file-control ~ .valid-feedback, .has-danger .is-focused .is-valid.custom-file-control ~ .valid-feedback, .has-danger.is-focused .is-valid.custom-file-control ~ .valid-tooltip, .has-danger .is-focused .is-valid.custom-file-control ~ .valid-tooltip, .was-validated .has-danger.is-focused .custom-file-control:valid ~ .valid-feedback, .was-validated .has-danger .is-focused .custom-file-control:valid ~ .valid-feedback, .was-validated .has-danger.is-focused .custom-file-control:valid ~ .valid-tooltip, .was-validated .has-danger .is-focused .custom-file-control:valid ~ .valid-tooltip, .was-validated .has-danger.is-focused .custom-select:valid ~ .valid-feedback, .was-validated .has-danger .is-focused .custom-select:valid ~ .valid-feedback, .was-validated .has-danger.is-focused .custom-select:valid ~ .valid-tooltip, .was-validated .has-danger .is-focused .custom-select:valid ~ .valid-tooltip, .was-validated .has-danger.is-focused .form-control:valid ~ .valid-feedback, .was-validated .has-danger .is-focused .form-control:valid ~ .valid-feedback, .was-validated .has-danger.is-focused .form-control:valid ~ .valid-tooltip, .was-validated .has-danger .is-focused .form-control:valid ~ .valid-tooltip { - display: block -} - -.has-danger.is-focused .form-check-input.is-valid ~ .form-check-label, .has-danger .is-focused .form-check-input.is-valid ~ .form-check-label, .was-validated .has-danger.is-focused .form-check-input:valid ~ .form-check-label, .was-validated .has-danger .is-focused .form-check-input:valid ~ .form-check-label { - color: #f44336 -} - -.has-danger.is-focused .form-check-input.is-valid ~ .valid-feedback, .has-danger .is-focused .form-check-input.is-valid ~ .valid-feedback, .has-danger.is-focused .form-check-input.is-valid ~ .valid-tooltip, .has-danger .is-focused .form-check-input.is-valid ~ .valid-tooltip, .was-validated .has-danger.is-focused .form-check-input:valid ~ .valid-feedback, .was-validated .has-danger .is-focused .form-check-input:valid ~ .valid-feedback, .was-validated .has-danger.is-focused .form-check-input:valid ~ .valid-tooltip, .was-validated .has-danger .is-focused .form-check-input:valid ~ .valid-tooltip { - display: block -} - -.has-danger.is-focused .custom-control-input.is-valid ~ .custom-control-label, .has-danger .is-focused .custom-control-input.is-valid ~ .custom-control-label, .was-validated .has-danger.is-focused .custom-control-input:valid ~ .custom-control-label, .was-validated .has-danger .is-focused .custom-control-input:valid ~ .custom-control-label { - color: #f44336 -} - -.has-danger.is-focused .custom-control-input.is-valid ~ .custom-control-label:before, .has-danger .is-focused .custom-control-input.is-valid ~ .custom-control-label:before, .was-validated .has-danger.is-focused .custom-control-input:valid ~ .custom-control-label:before, .was-validated .has-danger .is-focused .custom-control-input:valid ~ .custom-control-label:before { - background-color: #fbb4af -} - -.has-danger.is-focused .custom-control-input.is-valid ~ .valid-feedback, .has-danger .is-focused .custom-control-input.is-valid ~ .valid-feedback, .has-danger.is-focused .custom-control-input.is-valid ~ .valid-tooltip, .has-danger .is-focused .custom-control-input.is-valid ~ .valid-tooltip, .was-validated .has-danger.is-focused .custom-control-input:valid ~ .valid-feedback, .was-validated .has-danger .is-focused .custom-control-input:valid ~ .valid-feedback, .was-validated .has-danger.is-focused .custom-control-input:valid ~ .valid-tooltip, .was-validated .has-danger .is-focused .custom-control-input:valid ~ .valid-tooltip { - display: block -} - -.has-danger.is-focused .custom-control-input.is-valid:checked ~ .custom-control-label:before, .has-danger .is-focused .custom-control-input.is-valid:checked ~ .custom-control-label:before, .was-validated .has-danger.is-focused .custom-control-input:valid:checked ~ .custom-control-label:before, .was-validated .has-danger .is-focused .custom-control-input:valid:checked ~ .custom-control-label:before { - background-color: #f77066 -} - -.has-danger.is-focused .custom-control-input.is-valid:focus ~ .custom-control-label:before, .has-danger .is-focused .custom-control-input.is-valid:focus ~ .custom-control-label:before, .was-validated .has-danger.is-focused .custom-control-input:valid:focus ~ .custom-control-label:before, .was-validated .has-danger .is-focused .custom-control-input:valid:focus ~ .custom-control-label:before { - box-shadow: 0 0 0 1px #fafafa, 0 0 0 .2rem rgba(244, 67, 54, .25) -} - -.has-danger.is-focused .custom-file-input.is-valid ~ .custom-file-label, .has-danger .is-focused .custom-file-input.is-valid ~ .custom-file-label, .was-validated .has-danger.is-focused .custom-file-input:valid ~ .custom-file-label, .was-validated .has-danger .is-focused .custom-file-input:valid ~ .custom-file-label { - border-color: #f44336 -} - -.has-danger.is-focused .custom-file-input.is-valid ~ .custom-file-label:before, .has-danger .is-focused .custom-file-input.is-valid ~ .custom-file-label:before, .was-validated .has-danger.is-focused .custom-file-input:valid ~ .custom-file-label:before, .was-validated .has-danger .is-focused .custom-file-input:valid ~ .custom-file-label:before { - border-color: inherit -} - -.has-danger.is-focused .custom-file-input.is-valid ~ .valid-feedback, .has-danger .is-focused .custom-file-input.is-valid ~ .valid-feedback, .has-danger.is-focused .custom-file-input.is-valid ~ .valid-tooltip, .has-danger .is-focused .custom-file-input.is-valid ~ .valid-tooltip, .was-validated .has-danger.is-focused .custom-file-input:valid ~ .valid-feedback, .was-validated .has-danger .is-focused .custom-file-input:valid ~ .valid-feedback, .was-validated .has-danger.is-focused .custom-file-input:valid ~ .valid-tooltip, .was-validated .has-danger .is-focused .custom-file-input:valid ~ .valid-tooltip { - display: block -} - -.has-danger.is-focused .custom-file-input.is-valid:focus ~ .custom-file-label, .has-danger .is-focused .custom-file-input.is-valid:focus ~ .custom-file-label, .was-validated .has-danger.is-focused .custom-file-input:valid:focus ~ .custom-file-label, .was-validated .has-danger .is-focused .custom-file-input:valid:focus ~ .custom-file-label { - box-shadow: 0 0 0 .2rem rgba(244, 67, 54, .25) -} - -.has-danger.is-focused .bmd-label-placeholder, .has-danger .is-focused .bmd-label-placeholder, .has-danger.is-focused [class*=" bmd-label"], .has-danger .is-focused [class*=" bmd-label"], .has-danger.is-focused [class^=bmd-label], .has-danger .is-focused [class^=bmd-label] { - color: #f44336 -} - -.has-danger.is-focused .custom-file-control, .has-danger .is-focused .custom-file-control, .has-danger.is-focused .form-control, .has-danger .is-focused .form-control { - border-color: #f44336 -} - -.has-danger.is-focused .bmd-help, .has-danger .is-focused .bmd-help { - color: rgba(0, 0, 0, .54) -} - -.bmd-form-group { - position: relative; - padding-top: 1.75rem -} - -.bmd-form-group [class*=" bmd-label"], .bmd-form-group [class^=bmd-label] { - position: absolute; - pointer-events: none; - transition: all .3s ease -} - -.bmd-form-group [class*=" bmd-label"].bmd-label-floating, .bmd-form-group [class^=bmd-label].bmd-label-floating { - will-change: left, top, contents -} - -.bmd-form-group.is-filled .bmd-label-placeholder { - display: none -} - -.bmd-form-group.bmd-collapse-inline { - display: flex; - align-items: center; - padding: 0; - min-height: 2.1em -} - -.bmd-form-group.bmd-collapse-inline .collapse { - flex: 1; - display: none -} - -.bmd-form-group.bmd-collapse-inline .collapse.show { - max-width: 1200px -} - -.bmd-form-group.bmd-collapse-inline .collapse.show, .bmd-form-group.bmd-collapse-inline .collapsing, .bmd-form-group.bmd-collapse-inline .width:not(.collapse) { - display: block -} - -.bmd-form-group.bmd-collapse-inline .collapsing { - transition-duration: .2s; - transition-timing-function: cubic-bezier(.4, 0, .2, 1) -} - -.bmd-form-group .custom-file-control, .bmd-form-group .form-control, .bmd-form-group input::placeholder, .bmd-form-group label { - line-height: 1 -} - -.bmd-form-group .checkbox label, .bmd-form-group .radio label, .bmd-form-group .switch label, .bmd-form-group label.checkbox-inline, .bmd-form-group label.radio-inline { - line-height: 1.5 -} - -.bmd-form-group input::placeholder { - font-size: 1rem -} - -.bmd-form-group .checkbox label, .bmd-form-group .radio label, .bmd-form-group label { - font-size: 1rem -} - -.bmd-form-group .bmd-label-floating, .bmd-form-group .bmd-label-placeholder { - top: 2.1875rem -} - -.bmd-form-group .bmd-label-static, .bmd-form-group.is-filled .bmd-label-floating, .bmd-form-group .is-filled .bmd-label-floating, .bmd-form-group.is-focused .bmd-label-floating, .bmd-form-group .is-focused .bmd-label-floating { - top: 1rem; - left: 0; - font-size: .75rem -} - -.bmd-form-group .bmd-help { - margin-top: 0; - font-size: .75rem -} - -.bmd-form-group .form-control-danger.custom-file-control, .bmd-form-group .form-control-success.custom-file-control, .bmd-form-group .form-control-warning.custom-file-control, .bmd-form-group .form-control.form-control-danger, .bmd-form-group .form-control.form-control-success, .bmd-form-group .form-control.form-control-warning { - background-size: 0 100%, 100% 100%, .9375rem .9375rem -} - -.bmd-form-group .form-control-danger.custom-file-control, .bmd-form-group .form-control-danger.custom-file-control:focus, .bmd-form-group .form-control-success.custom-file-control, .bmd-form-group .form-control-success.custom-file-control:focus, .bmd-form-group .form-control-warning.custom-file-control, .bmd-form-group .form-control-warning.custom-file-control:focus, .bmd-form-group .form-control.form-control-danger, .bmd-form-group .form-control.form-control-danger:focus, .bmd-form-group .form-control.form-control-success, .bmd-form-group .form-control.form-control-success:focus, .bmd-form-group .form-control.form-control-warning, .bmd-form-group .form-control.form-control-warning:focus, .bmd-form-group.is-focused .bmd-form-group .form-control-danger.custom-file-control, .bmd-form-group.is-focused .bmd-form-group .form-control-success.custom-file-control, .bmd-form-group.is-focused .bmd-form-group .form-control-warning.custom-file-control, .bmd-form-group.is-focused .bmd-form-group .form-control.form-control-danger, .bmd-form-group.is-focused .bmd-form-group .form-control.form-control-success, .bmd-form-group.is-focused .bmd-form-group .form-control.form-control-warning { - padding-right: 0; - background-repeat: no-repeat, no-repeat; - background-position: bottom, 50% calc(100% - 1px), center right .46875rem -} - -.bmd-form-group .form-control-danger.custom-file-control:focus, .bmd-form-group .form-control-success.custom-file-control:focus, .bmd-form-group .form-control-warning.custom-file-control:focus, .bmd-form-group .form-control.form-control-danger:focus, .bmd-form-group .form-control.form-control-success:focus, .bmd-form-group .form-control.form-control-warning:focus, .bmd-form-group.is-focused .bmd-form-group .form-control-danger.custom-file-control, .bmd-form-group.is-focused .bmd-form-group .form-control-success.custom-file-control, .bmd-form-group.is-focused .bmd-form-group .form-control-warning.custom-file-control, .bmd-form-group.is-focused .bmd-form-group .form-control.form-control-danger, .bmd-form-group.is-focused .bmd-form-group .form-control.form-control-success, .bmd-form-group.is-focused .bmd-form-group .form-control.form-control-warning { - background-size: 100% 100%, 100% 100%, .9375rem .9375rem -} - -.bmd-form-group.bmd-form-group-sm { - padding-top: 1.40625rem -} - -.bmd-form-group.bmd-form-group-sm .custom-file-control, .bmd-form-group.bmd-form-group-sm .form-control, .bmd-form-group.bmd-form-group-sm input::placeholder, .bmd-form-group.bmd-form-group-sm label { - line-height: 1 -} - -.bmd-form-group.bmd-form-group-sm .checkbox label, .bmd-form-group.bmd-form-group-sm .radio label, .bmd-form-group.bmd-form-group-sm .switch label, .bmd-form-group.bmd-form-group-sm label.checkbox-inline, .bmd-form-group.bmd-form-group-sm label.radio-inline { - line-height: 1.5 -} - -.bmd-form-group.bmd-form-group-sm input::placeholder { - font-size: .875rem -} - -.bmd-form-group.bmd-form-group-sm .checkbox label, .bmd-form-group.bmd-form-group-sm .radio label, .bmd-form-group.bmd-form-group-sm label { - font-size: .875rem -} - -.bmd-form-group.bmd-form-group-sm .bmd-label-floating, .bmd-form-group.bmd-form-group-sm .bmd-label-placeholder { - top: 1.65625rem -} - -.bmd-form-group.bmd-form-group-sm .bmd-label-static, .bmd-form-group.bmd-form-group-sm.is-filled .bmd-label-floating, .bmd-form-group.bmd-form-group-sm .is-filled .bmd-label-floating, .bmd-form-group.bmd-form-group-sm.is-focused .bmd-label-floating, .bmd-form-group.bmd-form-group-sm .is-focused .bmd-label-floating { - top: .75rem; - left: 0; - font-size: .65625rem -} - -.bmd-form-group.bmd-form-group-sm .bmd-help { - margin-top: 0; - font-size: .65625rem -} - -.bmd-form-group.bmd-form-group-sm .form-control-danger.custom-file-control, .bmd-form-group.bmd-form-group-sm .form-control-success.custom-file-control, .bmd-form-group.bmd-form-group-sm .form-control-warning.custom-file-control, .bmd-form-group.bmd-form-group-sm .form-control.form-control-danger, .bmd-form-group.bmd-form-group-sm .form-control.form-control-success, .bmd-form-group.bmd-form-group-sm .form-control.form-control-warning { - background-size: 0 100%, 100% 100%, .6875rem .6875rem -} - -.bmd-form-group.bmd-form-group-sm .form-control-danger.custom-file-control, .bmd-form-group.bmd-form-group-sm .form-control-danger.custom-file-control:focus, .bmd-form-group.bmd-form-group-sm .form-control-success.custom-file-control, .bmd-form-group.bmd-form-group-sm .form-control-success.custom-file-control:focus, .bmd-form-group.bmd-form-group-sm .form-control-warning.custom-file-control, .bmd-form-group.bmd-form-group-sm .form-control-warning.custom-file-control:focus, .bmd-form-group.bmd-form-group-sm .form-control.form-control-danger, .bmd-form-group.bmd-form-group-sm .form-control.form-control-danger:focus, .bmd-form-group.bmd-form-group-sm .form-control.form-control-success, .bmd-form-group.bmd-form-group-sm .form-control.form-control-success:focus, .bmd-form-group.bmd-form-group-sm .form-control.form-control-warning, .bmd-form-group.bmd-form-group-sm .form-control.form-control-warning:focus, .bmd-form-group.is-focused .bmd-form-group.bmd-form-group-sm .form-control-danger.custom-file-control, .bmd-form-group.is-focused .bmd-form-group.bmd-form-group-sm .form-control-success.custom-file-control, .bmd-form-group.is-focused .bmd-form-group.bmd-form-group-sm .form-control-warning.custom-file-control, .bmd-form-group.is-focused .bmd-form-group.bmd-form-group-sm .form-control.form-control-danger, .bmd-form-group.is-focused .bmd-form-group.bmd-form-group-sm .form-control.form-control-success, .bmd-form-group.is-focused .bmd-form-group.bmd-form-group-sm .form-control.form-control-warning { - padding-right: 0; - background-repeat: no-repeat, no-repeat; - background-position: bottom, 50% calc(100% - 1px), center right .34375rem -} - -.bmd-form-group.bmd-form-group-sm .form-control-danger.custom-file-control:focus, .bmd-form-group.bmd-form-group-sm .form-control-success.custom-file-control:focus, .bmd-form-group.bmd-form-group-sm .form-control-warning.custom-file-control:focus, .bmd-form-group.bmd-form-group-sm .form-control.form-control-danger:focus, .bmd-form-group.bmd-form-group-sm .form-control.form-control-success:focus, .bmd-form-group.bmd-form-group-sm .form-control.form-control-warning:focus, .bmd-form-group.is-focused .bmd-form-group.bmd-form-group-sm .form-control-danger.custom-file-control, .bmd-form-group.is-focused .bmd-form-group.bmd-form-group-sm .form-control-success.custom-file-control, .bmd-form-group.is-focused .bmd-form-group.bmd-form-group-sm .form-control-warning.custom-file-control, .bmd-form-group.is-focused .bmd-form-group.bmd-form-group-sm .form-control.form-control-danger, .bmd-form-group.is-focused .bmd-form-group.bmd-form-group-sm .form-control.form-control-success, .bmd-form-group.is-focused .bmd-form-group.bmd-form-group-sm .form-control.form-control-warning { - background-size: 100% 100%, 100% 100%, .6875rem .6875rem -} - -.bmd-form-group.bmd-form-group-lg { - padding-top: 1.9375rem -} - -.bmd-form-group.bmd-form-group-lg .custom-file-control, .bmd-form-group.bmd-form-group-lg .form-control, .bmd-form-group.bmd-form-group-lg input::placeholder, .bmd-form-group.bmd-form-group-lg label { - line-height: 1 -} - -.bmd-form-group.bmd-form-group-lg .checkbox label, .bmd-form-group.bmd-form-group-lg .radio label, .bmd-form-group.bmd-form-group-lg .switch label, .bmd-form-group.bmd-form-group-lg label.checkbox-inline, .bmd-form-group.bmd-form-group-lg label.radio-inline { - line-height: 1.5 -} - -.bmd-form-group.bmd-form-group-lg input::placeholder { - font-size: 1.25rem -} - -.bmd-form-group.bmd-form-group-lg .checkbox label, .bmd-form-group.bmd-form-group-lg .radio label, .bmd-form-group.bmd-form-group-lg label { - font-size: 1.25rem -} - -.bmd-form-group.bmd-form-group-lg .bmd-label-floating, .bmd-form-group.bmd-form-group-lg .bmd-label-placeholder { - top: 2.5rem -} - -.bmd-form-group.bmd-form-group-lg .bmd-label-static, .bmd-form-group.bmd-form-group-lg.is-filled .bmd-label-floating, .bmd-form-group.bmd-form-group-lg .is-filled .bmd-label-floating, .bmd-form-group.bmd-form-group-lg.is-focused .bmd-label-floating, .bmd-form-group.bmd-form-group-lg .is-focused .bmd-label-floating { - top: 1rem; - left: 0; - font-size: .9375rem -} - -.bmd-form-group.bmd-form-group-lg .bmd-help { - margin-top: 0; - font-size: .9375rem -} - -.bmd-form-group.bmd-form-group-lg .form-control-danger.custom-file-control, .bmd-form-group.bmd-form-group-lg .form-control-success.custom-file-control, .bmd-form-group.bmd-form-group-lg .form-control-warning.custom-file-control, .bmd-form-group.bmd-form-group-lg .form-control.form-control-danger, .bmd-form-group.bmd-form-group-lg .form-control.form-control-success, .bmd-form-group.bmd-form-group-lg .form-control.form-control-warning { - background-size: 0 100%, 100% 100%, 1.1875rem 1.1875rem -} - -.bmd-form-group.bmd-form-group-lg .form-control-danger.custom-file-control, .bmd-form-group.bmd-form-group-lg .form-control-danger.custom-file-control:focus, .bmd-form-group.bmd-form-group-lg .form-control-success.custom-file-control, .bmd-form-group.bmd-form-group-lg .form-control-success.custom-file-control:focus, .bmd-form-group.bmd-form-group-lg .form-control-warning.custom-file-control, .bmd-form-group.bmd-form-group-lg .form-control-warning.custom-file-control:focus, .bmd-form-group.bmd-form-group-lg .form-control.form-control-danger, .bmd-form-group.bmd-form-group-lg .form-control.form-control-danger:focus, .bmd-form-group.bmd-form-group-lg .form-control.form-control-success, .bmd-form-group.bmd-form-group-lg .form-control.form-control-success:focus, .bmd-form-group.bmd-form-group-lg .form-control.form-control-warning, .bmd-form-group.bmd-form-group-lg .form-control.form-control-warning:focus, .bmd-form-group.is-focused .bmd-form-group.bmd-form-group-lg .form-control-danger.custom-file-control, .bmd-form-group.is-focused .bmd-form-group.bmd-form-group-lg .form-control-success.custom-file-control, .bmd-form-group.is-focused .bmd-form-group.bmd-form-group-lg .form-control-warning.custom-file-control, .bmd-form-group.is-focused .bmd-form-group.bmd-form-group-lg .form-control.form-control-danger, .bmd-form-group.is-focused .bmd-form-group.bmd-form-group-lg .form-control.form-control-success, .bmd-form-group.is-focused .bmd-form-group.bmd-form-group-lg .form-control.form-control-warning { - padding-right: 0; - background-repeat: no-repeat, no-repeat; - background-position: bottom, 50% calc(100% - 1px), center right .59375rem -} - -.bmd-form-group.bmd-form-group-lg .form-control-danger.custom-file-control:focus, .bmd-form-group.bmd-form-group-lg .form-control-success.custom-file-control:focus, .bmd-form-group.bmd-form-group-lg .form-control-warning.custom-file-control:focus, .bmd-form-group.bmd-form-group-lg .form-control.form-control-danger:focus, .bmd-form-group.bmd-form-group-lg .form-control.form-control-success:focus, .bmd-form-group.bmd-form-group-lg .form-control.form-control-warning:focus, .bmd-form-group.is-focused .bmd-form-group.bmd-form-group-lg .form-control-danger.custom-file-control, .bmd-form-group.is-focused .bmd-form-group.bmd-form-group-lg .form-control-success.custom-file-control, .bmd-form-group.is-focused .bmd-form-group.bmd-form-group-lg .form-control-warning.custom-file-control, .bmd-form-group.is-focused .bmd-form-group.bmd-form-group-lg .form-control.form-control-danger, .bmd-form-group.is-focused .bmd-form-group.bmd-form-group-lg .form-control.form-control-success, .bmd-form-group.is-focused .bmd-form-group.bmd-form-group-lg .form-control.form-control-warning { - background-size: 100% 100%, 100% 100%, 1.1875rem 1.1875rem -} - -.custom-file-control, .form-control, input::placeholder, label { - line-height: 1 -} - -.checkbox label, .radio label, .switch label, label.checkbox-inline, label.radio-inline { - line-height: 1.5 -} - -input::placeholder { - font-size: 1rem -} - -.checkbox label, .radio label, label { - font-size: 1rem -} - -.bmd-label-floating, .bmd-label-placeholder { - top: 2.1875rem -} - -.bmd-label-static, .is-filled .bmd-label-floating, .is-focused .bmd-label-floating { - top: 1rem; - left: 0; - font-size: .75rem -} - -.bmd-help { - margin-top: 0; - font-size: .75rem -} - -.form-control-danger.custom-file-control, .form-control-success.custom-file-control, .form-control-warning.custom-file-control, .form-control.form-control-danger, .form-control.form-control-success, .form-control.form-control-warning { - background-size: 0 100%, 100% 100%, .9375rem .9375rem -} - -.bmd-form-group.is-focused .form-control-danger.custom-file-control, .bmd-form-group.is-focused .form-control-success.custom-file-control, .bmd-form-group.is-focused .form-control-warning.custom-file-control, .bmd-form-group.is-focused .form-control.form-control-danger, .bmd-form-group.is-focused .form-control.form-control-success, .bmd-form-group.is-focused .form-control.form-control-warning, .form-control-danger.custom-file-control, .form-control-danger.custom-file-control:focus, .form-control-success.custom-file-control, .form-control-success.custom-file-control:focus, .form-control-warning.custom-file-control, .form-control-warning.custom-file-control:focus, .form-control.form-control-danger, .form-control.form-control-danger:focus, .form-control.form-control-success, .form-control.form-control-success:focus, .form-control.form-control-warning, .form-control.form-control-warning:focus { - padding-right: 0; - background-repeat: no-repeat, no-repeat; - background-position: bottom, 50% calc(100% - 1px), center right .46875rem -} - -.bmd-form-group.is-focused .form-control-danger.custom-file-control, .bmd-form-group.is-focused .form-control-success.custom-file-control, .bmd-form-group.is-focused .form-control-warning.custom-file-control, .bmd-form-group.is-focused .form-control.form-control-danger, .bmd-form-group.is-focused .form-control.form-control-success, .bmd-form-group.is-focused .form-control.form-control-warning, .form-control-danger.custom-file-control:focus, .form-control-success.custom-file-control:focus, .form-control-warning.custom-file-control:focus, .form-control.form-control-danger:focus, .form-control.form-control-success:focus, .form-control.form-control-warning:focus { - background-size: 100% 100%, 100% 100%, .9375rem .9375rem -} - -select, select.custom-file-control, select.form-control { - -moz-appearance: none; - -webkit-appearance: none -} - -@media (min-width: 576px) { - .form-inline .input-group { - display: inline-flex; - align-items: center - } -} - -.input-group .input-group-addon { - display: flex; - justify-content: center; - align-items: center; - background-color: transparent; - border-color: transparent -} - -.input-group .input-group-addon + input, .input-group input + .input-group-addon { - margin-left: .75rem -} - -.list-group { - display: flex; - flex-direction: column; - flex-grow: 1; - padding: .5rem 0 -} - -.list-group.bmd-list-group-sm { - padding: .25rem 0 -} - -.list-group.bmd-list-group-sm .list-group-item { - padding: .5rem 1rem -} - -.bmd-list-group-col { - display: flex; - flex-direction: column; - min-width: 0 -} - -.list-group-item { - display: flex; - flex-flow: row wrap; - align-items: center; - padding: 1rem; - line-height: 1 -} - -.list-group-item .list-group-item-text { - min-width: 0; - max-height: 2.188rem; - overflow: hidden; - text-overflow: ellipsis -} - -.list-group-item :first-child { - margin-right: 2rem -} - -.list-group-item > * ~ .label:last-child, .list-group-item > * ~ .material-icons:last-child, .list-group-item > .pull-lg-right, .list-group-item > .pull-md-right, .list-group-item > .pull-sm-right, .list-group-item > .pull-xl-right, .list-group-item > .pull-xs-right { - margin-right: 0; - margin-left: auto -} - -.list-group-item .material-icons.pull-lg-right, .list-group-item .material-icons.pull-md-right, .list-group-item .material-icons.pull-sm-right, .list-group-item .material-icons.pull-xl-right, .list-group-item .material-icons.pull-xs-right, .list-group-item .material-icons ~ .material-icons:last-child { - padding-left: 1rem -} - -.list-group-item .list-group-item-text { - font-size: .875rem; - color: rgba(0, 0, 0, .54) -} - -.table-inverse { - color: hsla(0, 0%, 100%, .84) -} - -.table thead th { - font-size: .95rem; - font-weight: 500; - color: rgba(0, 0, 0, .54); - border-top-width: 0; - border-bottom-width: 1px -} - -.table-inverse thead th, thead.thead-inverse th { - color: hsla(0, 0%, 100%, .54) -} - -.table-inverse td, .table-inverse th, .table-inverse thead th { - border-color: hsla(0, 0%, 100%, .06) -} - -.nav-link { - text-transform: uppercase -} - -.navbar-nav .nav-link { - padding: .5321rem; - font-size: .875rem; - font-weight: 400 -} - -.nav-pills, .nav-tabs { - border: 0 -} - -.nav-pills .nav-link, .nav-tabs .nav-link { - padding: 1.4286em .8575em; - font-size: .875rem; - font-weight: 500; - border: 0 -} - -.nav-pills .nav-item.show .nav-link, .nav-pills .nav-link.active, .nav-tabs .nav-item.show .nav-link, .nav-tabs .nav-link.active { - background-color: transparent; - color: inherit -} - -.nav-tabs .nav-link { - border-bottom: .214rem solid transparent; - color: rgba(0, 0, 0, .54) -} - -.nav-tabs .nav-link.active { - color: rgba(0, 0, 0, .87) -} - -.nav-tabs .nav-link.active, .nav-tabs .nav-link.active:focus, .nav-tabs .nav-link.active:hover { - border-color: theme-color(primary) -} - -.nav-tabs .nav-link.disabled, .nav-tabs .nav-link.disabled:focus, .nav-tabs .nav-link.disabled:hover { - color: rgba(0, 0, 0, .26) -} - -.nav-tabs.bg-primary .nav-link { - color: #fff -} - -.nav-tabs.bg-primary .nav-link.active { - color: #fff; - border-color: #fff -} - -.nav-tabs.bg-primary .nav-link.active:focus, .nav-tabs.bg-primary .nav-link.active:hover { - border-color: #fff -} - -.nav-tabs.bg-primary .nav-link.disabled, .nav-tabs.bg-primary .nav-link.disabled:focus, .nav-tabs.bg-primary .nav-link.disabled:hover { - color: hsla(0, 0%, 100%, .84) -} - -.nav-tabs.bg-dark .nav-link { - color: #fff -} - -.nav-tabs.bg-dark .nav-link.active { - color: #fff; - border-color: #fff -} - -.nav-tabs.bg-dark .nav-link.active:focus, .nav-tabs.bg-dark .nav-link.active:hover { - border-color: #fff -} - -.nav-tabs.bg-dark .nav-link.disabled, .nav-tabs.bg-dark .nav-link.disabled:focus, .nav-tabs.bg-dark .nav-link.disabled:hover { - color: hsla(0, 0%, 100%, .84) -} - -.nav .nav-item { - position: relative -} - -.navbar { - z-index: 1; - border: 0; - border-radius: 0; - box-shadow: 0 2px 2px 0 rgba(0, 0, 0, .14), 0 3px 1px -2px rgba(0, 0, 0, .2), 0 1px 5px 0 rgba(0, 0, 0, .12) -} - -.navbar .navbar-brand { - position: relative -} - -.navbar > .container { - flex: 1 -} - -.navbar-dark .custom-file-control, .navbar-dark .form-control { - color: #fff -} - -.navbar-dark .custom-file-control::placeholder, .navbar-dark .form-control::placeholder { - color: hsla(0, 0%, 100%, .5) -} - -.alert { - border: 0; - border-radius: 0 -} - -.progress { - height: 4px; - background: #c8c8c8; - border-radius: 0 -} - -.progress, .progress .progress-bar { - box-shadow: none -} - -.page-item:first-child .page-link { - border-top-left-radius: 4em; - border-bottom-left-radius: 4em -} - -.page-item:last-child .page-link { - border-top-right-radius: 4em; - border-bottom-right-radius: 4em; - margin-right: 0 -} - -.page-item:first-child .page-link, .page-item:last-child .page-link { - display: flex; - justify-content: center; - padding-right: 0; - padding-left: 0 -} - -.page-item:first-child .page-link > .material-icons, .page-item:last-child .page-link > .material-icons { - font-size: 1.5em -} - -.page-link { - min-width: 2.6em; - margin-right: .5em; - text-align: center; - border-radius: 4em -} - -.page-link:focus, .page-link:hover { - text-decoration: none -} - -.pagination-lg .page-link { - min-width: 2.8em; - padding: .75rem 0; - font-size: 1.25rem; - line-height: 1.5 -} - -.pagination-lg .page-item:first-child .page-link { - border-top-left-radius: 4em; - border-bottom-left-radius: 4em -} - -.pagination-lg .page-item:last-child .page-link { - border-top-right-radius: 4em; - border-bottom-right-radius: 4em -} - -.pagination-sm .page-link { - min-width: 2.2em; - padding: .25rem 0; - font-size: .875rem; - line-height: 1.5 -} - -.pagination-sm .page-item:first-child .page-link { - border-top-left-radius: 4em; - border-bottom-left-radius: 4em -} - -.pagination-sm .page-item:last-child .page-link { - border-top-right-radius: 4em; - border-bottom-right-radius: 4em -} - -h6 { - font-weight: 500 -} - -.tooltip-arrow { - display: none -} - -.card { - border: 0; - box-shadow: 0 2px 2px 0 rgba(0, 0, 0, .14), 0 3px 1px -2px rgba(0, 0, 0, .2), 0 1px 5px 0 rgba(0, 0, 0, .12) -} - -.card .card-header { - padding: 1rem; - background-color: transparent -} - -.card .card-block { - padding: 1.5rem 1rem 1rem -} - -.card h1.card-title, .card h2.card-title, .card h3.card-title, .card h4.card-title, .card h5.card-title, .card h6.card-title { - font-size: 1.5rem; - font-weight: 300 -} - -.card .bmd-card-actions, .card it may be necessary .card-footer { - padding: .5rem -} - -.card.bmd-card-raised { - box-shadow: 0 8px 10px 1px rgba(0, 0, 0, .14), 0 3px 14px 2px rgba(0, 0, 0, .12), 0 5px 5px -3px rgba(0, 0, 0, .2) -} - -@media (min-width: 992px) { - .card.bmd-card-flat { - box-shadow: none - } -} - -.modal-content { - border: 0; - border-radius: .125rem -} - -.modal-content .modal-header { - padding: 24px 24px 0; - border-bottom: 0 -} - -.modal-content .modal-body { - padding: 20px 24px 24px -} - -.modal-content .modal-footer { - padding: 8px 8px 8px 24px; - border-top: 0 -} - -.modal-content .modal-footer .btn, .modal-content .modal-footer .custom-file-control:before { - margin: 0 -} - -.modal-content .modal-footer .btn + .btn, .modal-content .modal-footer .btn + .custom-file-control:before, .modal-content .modal-footer .custom-file-control:before + .btn, .modal-content .modal-footer .custom-file-control:before + .custom-file-control:before { - margin-left: 8px -} - -.dropdown-menu { - display: none; - padding: .25rem 0; - border: 0; - opacity: 0; - transform: scale(0); - transform-origin: 0 0; - will-change: transform, opacity; - transition: transform .3s cubic-bezier(.4, 0, .2, 1), opacity .2s cubic-bezier(.4, 0, .2, 1) -} - -@media (min-width: 768px) { - .dropdown-menu { - padding: .5rem 0 - } -} - -.dropdown-menu.showing { - animation-name: d; - animation-duration: .3s; - animation-fill-mode: forwards; - animation-timing-function: cubic-bezier(.4, 0, .2, 1) -} - -.dropdown-menu.show, .open > .dropdown-menu { - display: block; - opacity: 1; - transform: scale(1) -} - -.dropdown-menu.hiding { - display: block; - opacity: 0; - transform: scale(0) -} - -.dropdown-menu.dropdown-menu-left, .dropdown-menu[x-placement=bottom-start] { - transform-origin: 0 0 -} - -.dropdown-menu.dropdown-menu-right, .dropdown-menu[x-placement=bottom-end] { - transform-origin: 100% 0 -} - -.dropdown-menu[x-placement=top-start] { - transform-origin: 0 100% -} - -.dropdown-menu[x-placement=top-end] { - transform-origin: 100% 100% -} - -.dropdown-menu .dropdown-item { - position: relative; - display: flex; - flex-flow: row wrap; - align-items: center; - min-width: 7rem; - max-width: 17.5rem; - min-height: 3rem; - padding: .8rem 1rem; - overflow: hidden; - line-height: 1; - text-overflow: ellipsis; - word-wrap: break-word -} - -.dropdown-menu .dropdown-item.active, .dropdown-menu .dropdown-item:active { - background-color: inherit; - color: inherit -} - -@media (min-width: 768px) { - .dropdown-menu .dropdown-item { - padding-right: 1.5rem; - padding-left: 1.5rem - } -} - -.dropdown-toggle.bmd-btn-fab:after, .dropdown-toggle.bmd-btn-icon:after { - display: none -} - -.dropdown-toggle.bmd-btn-fab ~ .dropdown-menu.dropdown-menu-top-left, .dropdown-toggle.bmd-btn-fab ~ .dropdown-menu.dropdown-menu-top-right, .dropdown-toggle.bmd-btn-icon ~ .dropdown-menu.dropdown-menu-top-left, .dropdown-toggle.bmd-btn-icon ~ .dropdown-menu.dropdown-menu-top-right { - bottom: 2rem -} - -.dropdown-toggle.bmd-btn-fab-sm ~ .dropdown-menu.dropdown-menu-top-left, .dropdown-toggle.bmd-btn-fab-sm ~ .dropdown-menu.dropdown-menu-top-right { - bottom: 2.5rem -} - -.dropdown-toggle.bmd-btn-icon ~ .dropdown-menu { - margin: 0 -} - -.show .dropdown-toggle.btn, .show .dropdown-toggle.btn-danger, .show .dropdown-toggle.btn-info, .show .dropdown-toggle.btn-secondary, .show .dropdown-toggle.btn-success, .show .dropdown-toggle.btn-warning, .show .dropdown-toggle.custom-file-control:before { - background-color: transparent -} - -@keyframes d { - 0% { - opacity: 0; - transform: scale(0) - } - to { - opacity: 1; - transform: scale(1) - } -} - -.bmd-layout-drawer { - position: absolute; - z-index: 5; - box-sizing: border-box; - display: flex; - flex-direction: column; - flex-wrap: nowrap; - max-height: 100%; - overflow: visible; - overflow-y: auto; - font-size: .875rem; - background: #fff; - transition: transform; - will-change: transform; - transform-style: preserve-3d; - box-shadow: 0 2px 2px 0 rgba(0, 0, 0, .14), 0 3px 1px -2px rgba(0, 0, 0, .2), 0 1px 5px 0 rgba(0, 0, 0, .12); - transition-duration: .2s; - transition-timing-function: cubic-bezier(.4, 0, .2, 1) -} - -.bmd-layout-drawer > * { - flex-shrink: 0 -} - -.bmd-layout-drawer > header { - display: flex; - flex-direction: column; - justify-content: flex-end; - padding: 1rem -} - -.bmd-layout-drawer > header .navbar-brand { - padding-left: 1.5rem; - font-size: 1.25rem -} - -.bmd-layout-drawer > .list-group { - padding-top: 1rem -} - -.bmd-layout-drawer > .list-group .list-group-item { - padding-right: 2.5rem; - padding-left: 2.5rem; - font-size: .8125rem; - font-weight: 500 -} - -.bmd-drawer-f-l > .bmd-layout-drawer { - top: 0; - left: 0; - width: 240px; - height: 100%; - transform: translateX(-250px) -} - -.bmd-drawer-f-l > .bmd-layout-content, .bmd-drawer-f-l > .bmd-layout-header { - margin-left: 0 -} - -.bmd-drawer-f-r > .bmd-layout-drawer { - top: 0; - right: 0; - width: 240px; - height: 100%; - transform: translateX(250px) -} - -.bmd-drawer-f-r > .bmd-layout-content, .bmd-drawer-f-r > .bmd-layout-header { - margin-right: 0 -} - -.bmd-drawer-f-t > .bmd-layout-drawer { - top: 0; - left: 0; - width: 100%; - height: 100px; - transform: translateY(-110px) -} - -.bmd-drawer-f-t > .bmd-layout-content { - margin-top: 0 -} - -.bmd-drawer-f-b > .bmd-layout-drawer { - bottom: 0; - left: 0; - width: 100%; - height: 100px; - transform: translateY(110px) -} - -.bmd-drawer-f-b > .bmd-layout-content { - margin-bottom: 0 -} - -:not(.bmd-drawer-out).bmd-drawer-in.bmd-drawer-f-l > .bmd-layout-header { - width: calc(100% - 240px); - margin-left: 240px -} - -:not(.bmd-drawer-out).bmd-drawer-in.bmd-drawer-f-l > .bmd-layout-drawer { - transform: translateX(0) -} - -:not(.bmd-drawer-out).bmd-drawer-in.bmd-drawer-f-l > .bmd-layout-content { - margin-left: 240px -} - -:not(.bmd-drawer-out).bmd-drawer-in.bmd-drawer-f-r > .bmd-layout-header { - width: calc(100% - 240px); - margin-right: 240px -} - -:not(.bmd-drawer-out).bmd-drawer-in.bmd-drawer-f-r > .bmd-layout-drawer { - transform: translateX(0) -} - -:not(.bmd-drawer-out).bmd-drawer-in.bmd-drawer-f-r > .bmd-layout-content { - margin-right: 240px -} - -:not(.bmd-drawer-out).bmd-drawer-in.bmd-drawer-f-t > .bmd-layout-header { - margin-top: 100px -} - -:not(.bmd-drawer-out).bmd-drawer-in.bmd-drawer-f-b > .bmd-layout-drawer, :not(.bmd-drawer-out).bmd-drawer-in.bmd-drawer-f-t > .bmd-layout-drawer { - transform: translateY(0) -} - -:not(.bmd-drawer-out).bmd-drawer-in.bmd-drawer-f-b > .bmd-layout-content { - margin-bottom: 100px -} - -:not(.bmd-drawer-out).bmd-drawer-overlay.bmd-drawer-overlay.bmd-drawer-f-l > .bmd-layout-content, :not(.bmd-drawer-out).bmd-drawer-overlay.bmd-drawer-overlay.bmd-drawer-f-l > .bmd-layout-header { - width: 100%; - margin-left: 0 -} - -:not(.bmd-drawer-out).bmd-drawer-overlay.bmd-drawer-overlay.bmd-drawer-f-r > .bmd-layout-content, :not(.bmd-drawer-out).bmd-drawer-overlay.bmd-drawer-overlay.bmd-drawer-f-r > .bmd-layout-header { - width: 100%; - margin-right: 0 -} - -:not(.bmd-drawer-out).bmd-drawer-overlay.bmd-drawer-overlay > .bmd-layout-backdrop .in { - visibility: visible; - background-color: rgba(0, 0, 0, .5) -} - -@supports (pointer-events:auto) { - :not(.bmd-drawer-out).bmd-drawer-overlay.bmd-drawer-overlay > .bmd-layout-backdrop.in { - pointer-events: auto; - opacity: 1 - } -} - -:not(.bmd-drawer-out).bmd-drawer-overlay.bmd-drawer-overlay.bmd-drawer-f-t > .bmd-layout-header { - margin-top: 0 -} - -:not(.bmd-drawer-out).bmd-drawer-overlay.bmd-drawer-overlay.bmd-drawer-f-b > .bmd-layout-content { - margin-bottom: 0 -} - -@media (min-width: 576px) { - :not(.bmd-drawer-out).bmd-drawer-in-sm-up.bmd-drawer-f-l > .bmd-layout-header { - width: calc(100% - 240px); - margin-left: 240px - } - - :not(.bmd-drawer-out).bmd-drawer-in-sm-up.bmd-drawer-f-l > .bmd-layout-drawer { - transform: translateX(0) - } - - :not(.bmd-drawer-out).bmd-drawer-in-sm-up.bmd-drawer-f-l > .bmd-layout-content { - margin-left: 240px - } - - :not(.bmd-drawer-out).bmd-drawer-in-sm-up.bmd-drawer-f-r > .bmd-layout-header { - width: calc(100% - 240px); - margin-right: 240px - } - - :not(.bmd-drawer-out).bmd-drawer-in-sm-up.bmd-drawer-f-r > .bmd-layout-drawer { - transform: translateX(0) - } - - :not(.bmd-drawer-out).bmd-drawer-in-sm-up.bmd-drawer-f-r > .bmd-layout-content { - margin-right: 240px - } -} - -@media (min-width: 576px) { - :not(.bmd-drawer-out).bmd-drawer-in-sm-up.bmd-drawer-f-t > .bmd-layout-header { - margin-top: 100px - } - - :not(.bmd-drawer-out).bmd-drawer-in-sm-up.bmd-drawer-f-b > .bmd-layout-drawer, :not(.bmd-drawer-out).bmd-drawer-in-sm-up.bmd-drawer-f-t > .bmd-layout-drawer { - transform: translateY(0) - } - - :not(.bmd-drawer-out).bmd-drawer-in-sm-up.bmd-drawer-f-b > .bmd-layout-content { - margin-bottom: 100px - } -} - -@media (max-width: 767.98px) { - :not(.bmd-drawer-out).bmd-drawer-overlay-sm-down > .bmd-layout-backdrop .in { - visibility: visible; - background-color: rgba(0, 0, 0, .5) - } - - @supports (pointer-events:auto) { - :not(.bmd-drawer-out).bmd-drawer-overlay-sm-down > .bmd-layout-backdrop.in { - pointer-events: auto; - opacity: 1 - } - }:not(.bmd-drawer-out).bmd-drawer-overlay-sm-down.bmd-drawer-f-l > .bmd-layout-content, :not(.bmd-drawer-out).bmd-drawer-overlay-sm-down.bmd-drawer-f-l > .bmd-layout-header { - width: 100%; - margin-left: 0 - } - - :not(.bmd-drawer-out).bmd-drawer-overlay-sm-down.bmd-drawer-f-r > .bmd-layout-content, :not(.bmd-drawer-out).bmd-drawer-overlay-sm-down.bmd-drawer-f-r > .bmd-layout-header { - width: 100%; - margin-right: 0 - } -} - -@media (max-width: 767.98px) { - :not(.bmd-drawer-out).bmd-drawer-overlay-sm-down > .bmd-layout-backdrop .in { - visibility: visible; - background-color: rgba(0, 0, 0, .5) - } - - @supports (pointer-events:auto) { - :not(.bmd-drawer-out).bmd-drawer-overlay-sm-down > .bmd-layout-backdrop.in { - pointer-events: auto; - opacity: 1 - } - }:not(.bmd-drawer-out).bmd-drawer-overlay-sm-down.bmd-drawer-f-t > .bmd-layout-header { - margin-top: 0 - } - - :not(.bmd-drawer-out).bmd-drawer-overlay-sm-down.bmd-drawer-f-b > .bmd-layout-content { - margin-bottom: 0 - } -} - -@media (min-width: 768px) { - :not(.bmd-drawer-out).bmd-drawer-in-md-up.bmd-drawer-f-l > .bmd-layout-header { - width: calc(100% - 240px); - margin-left: 240px - } - - :not(.bmd-drawer-out).bmd-drawer-in-md-up.bmd-drawer-f-l > .bmd-layout-drawer { - transform: translateX(0) - } - - :not(.bmd-drawer-out).bmd-drawer-in-md-up.bmd-drawer-f-l > .bmd-layout-content { - margin-left: 240px - } - - :not(.bmd-drawer-out).bmd-drawer-in-md-up.bmd-drawer-f-r > .bmd-layout-header { - width: calc(100% - 240px); - margin-right: 240px - } - - :not(.bmd-drawer-out).bmd-drawer-in-md-up.bmd-drawer-f-r > .bmd-layout-drawer { - transform: translateX(0) - } - - :not(.bmd-drawer-out).bmd-drawer-in-md-up.bmd-drawer-f-r > .bmd-layout-content { - margin-right: 240px - } -} - -@media (min-width: 768px) { - :not(.bmd-drawer-out).bmd-drawer-in-md-up.bmd-drawer-f-t > .bmd-layout-header { - margin-top: 100px - } - - :not(.bmd-drawer-out).bmd-drawer-in-md-up.bmd-drawer-f-b > .bmd-layout-drawer, :not(.bmd-drawer-out).bmd-drawer-in-md-up.bmd-drawer-f-t > .bmd-layout-drawer { - transform: translateY(0) - } - - :not(.bmd-drawer-out).bmd-drawer-in-md-up.bmd-drawer-f-b > .bmd-layout-content { - margin-bottom: 100px - } -} - -@media (max-width: 991.98px) { - :not(.bmd-drawer-out).bmd-drawer-overlay-md-down > .bmd-layout-backdrop .in { - visibility: visible; - background-color: rgba(0, 0, 0, .5) - } - - @supports (pointer-events:auto) { - :not(.bmd-drawer-out).bmd-drawer-overlay-md-down > .bmd-layout-backdrop.in { - pointer-events: auto; - opacity: 1 - } - }:not(.bmd-drawer-out).bmd-drawer-overlay-md-down.bmd-drawer-f-l > .bmd-layout-content, :not(.bmd-drawer-out).bmd-drawer-overlay-md-down.bmd-drawer-f-l > .bmd-layout-header { - width: 100%; - margin-left: 0 - } - - :not(.bmd-drawer-out).bmd-drawer-overlay-md-down.bmd-drawer-f-r > .bmd-layout-content, :not(.bmd-drawer-out).bmd-drawer-overlay-md-down.bmd-drawer-f-r > .bmd-layout-header { - width: 100%; - margin-right: 0 - } -} - -@media (max-width: 991.98px) { - :not(.bmd-drawer-out).bmd-drawer-overlay-md-down > .bmd-layout-backdrop .in { - visibility: visible; - background-color: rgba(0, 0, 0, .5) - } - - @supports (pointer-events:auto) { - :not(.bmd-drawer-out).bmd-drawer-overlay-md-down > .bmd-layout-backdrop.in { - pointer-events: auto; - opacity: 1 - } - }:not(.bmd-drawer-out).bmd-drawer-overlay-md-down.bmd-drawer-f-t > .bmd-layout-header { - margin-top: 0 - } - - :not(.bmd-drawer-out).bmd-drawer-overlay-md-down.bmd-drawer-f-b > .bmd-layout-content { - margin-bottom: 0 - } -} - -@media (min-width: 992px) { - :not(.bmd-drawer-out).bmd-drawer-in-lg-up.bmd-drawer-f-l > .bmd-layout-header { - width: calc(100% - 240px); - margin-left: 240px - } - - :not(.bmd-drawer-out).bmd-drawer-in-lg-up.bmd-drawer-f-l > .bmd-layout-drawer { - transform: translateX(0) - } - - :not(.bmd-drawer-out).bmd-drawer-in-lg-up.bmd-drawer-f-l > .bmd-layout-content { - margin-left: 240px - } - - :not(.bmd-drawer-out).bmd-drawer-in-lg-up.bmd-drawer-f-r > .bmd-layout-header { - width: calc(100% - 240px); - margin-right: 240px - } - - :not(.bmd-drawer-out).bmd-drawer-in-lg-up.bmd-drawer-f-r > .bmd-layout-drawer { - transform: translateX(0) - } - - :not(.bmd-drawer-out).bmd-drawer-in-lg-up.bmd-drawer-f-r > .bmd-layout-content { - margin-right: 240px - } -} - -@media (min-width: 992px) { - :not(.bmd-drawer-out).bmd-drawer-in-lg-up.bmd-drawer-f-t > .bmd-layout-header { - margin-top: 100px - } - - :not(.bmd-drawer-out).bmd-drawer-in-lg-up.bmd-drawer-f-b > .bmd-layout-drawer, :not(.bmd-drawer-out).bmd-drawer-in-lg-up.bmd-drawer-f-t > .bmd-layout-drawer { - transform: translateY(0) - } - - :not(.bmd-drawer-out).bmd-drawer-in-lg-up.bmd-drawer-f-b > .bmd-layout-content { - margin-bottom: 100px - } -} - -@media (max-width: 1199.98px) { - :not(.bmd-drawer-out).bmd-drawer-overlay-lg-down > .bmd-layout-backdrop .in { - visibility: visible; - background-color: rgba(0, 0, 0, .5) - } - - @supports (pointer-events:auto) { - :not(.bmd-drawer-out).bmd-drawer-overlay-lg-down > .bmd-layout-backdrop.in { - pointer-events: auto; - opacity: 1 - } - }:not(.bmd-drawer-out).bmd-drawer-overlay-lg-down.bmd-drawer-f-l > .bmd-layout-content, :not(.bmd-drawer-out).bmd-drawer-overlay-lg-down.bmd-drawer-f-l > .bmd-layout-header { - width: 100%; - margin-left: 0 - } - - :not(.bmd-drawer-out).bmd-drawer-overlay-lg-down.bmd-drawer-f-r > .bmd-layout-content, :not(.bmd-drawer-out).bmd-drawer-overlay-lg-down.bmd-drawer-f-r > .bmd-layout-header { - width: 100%; - margin-right: 0 - } -} - -@media (max-width: 1199.98px) { - :not(.bmd-drawer-out).bmd-drawer-overlay-lg-down > .bmd-layout-backdrop .in { - visibility: visible; - background-color: rgba(0, 0, 0, .5) - } - - @supports (pointer-events:auto) { - :not(.bmd-drawer-out).bmd-drawer-overlay-lg-down > .bmd-layout-backdrop.in { - pointer-events: auto; - opacity: 1 - } - }:not(.bmd-drawer-out).bmd-drawer-overlay-lg-down.bmd-drawer-f-t > .bmd-layout-header { - margin-top: 0 - } - - :not(.bmd-drawer-out).bmd-drawer-overlay-lg-down.bmd-drawer-f-b > .bmd-layout-content { - margin-bottom: 0 - } -} - -@media (min-width: 1200px) { - :not(.bmd-drawer-out).bmd-drawer-in-xl-up.bmd-drawer-f-l > .bmd-layout-header { - width: calc(100% - 240px); - margin-left: 240px - } - - :not(.bmd-drawer-out).bmd-drawer-in-xl-up.bmd-drawer-f-l > .bmd-layout-drawer { - transform: translateX(0) - } - - :not(.bmd-drawer-out).bmd-drawer-in-xl-up.bmd-drawer-f-l > .bmd-layout-content { - margin-left: 240px - } - - :not(.bmd-drawer-out).bmd-drawer-in-xl-up.bmd-drawer-f-r > .bmd-layout-header { - width: calc(100% - 240px); - margin-right: 240px - } - - :not(.bmd-drawer-out).bmd-drawer-in-xl-up.bmd-drawer-f-r > .bmd-layout-drawer { - transform: translateX(0) - } - - :not(.bmd-drawer-out).bmd-drawer-in-xl-up.bmd-drawer-f-r > .bmd-layout-content { - margin-right: 240px - } -} - -@media (min-width: 1200px) { - :not(.bmd-drawer-out).bmd-drawer-in-xl-up.bmd-drawer-f-t > .bmd-layout-header { - margin-top: 100px - } - - :not(.bmd-drawer-out).bmd-drawer-in-xl-up.bmd-drawer-f-b > .bmd-layout-drawer, :not(.bmd-drawer-out).bmd-drawer-in-xl-up.bmd-drawer-f-t > .bmd-layout-drawer { - transform: translateY(0) - } - - :not(.bmd-drawer-out).bmd-drawer-in-xl-up.bmd-drawer-f-b > .bmd-layout-content { - margin-bottom: 100px - } -} - -:not(.bmd-drawer-out).bmd-drawer-overlay-xl-down.bmd-drawer-f-l > .bmd-layout-content, :not(.bmd-drawer-out).bmd-drawer-overlay-xl-down.bmd-drawer-f-l > .bmd-layout-header { - width: 100%; - margin-left: 0 -} - -:not(.bmd-drawer-out).bmd-drawer-overlay-xl-down.bmd-drawer-f-r > .bmd-layout-content, :not(.bmd-drawer-out).bmd-drawer-overlay-xl-down.bmd-drawer-f-r > .bmd-layout-header { - width: 100%; - margin-right: 0 -} - -:not(.bmd-drawer-out).bmd-drawer-overlay-xl-down > .bmd-layout-backdrop .in { - visibility: visible; - background-color: rgba(0, 0, 0, .5) -} - -@supports (pointer-events:auto) { - :not(.bmd-drawer-out).bmd-drawer-overlay-xl-down > .bmd-layout-backdrop.in { - pointer-events: auto; - opacity: 1 - } -} - -:not(.bmd-drawer-out).bmd-drawer-overlay-xl-down.bmd-drawer-f-t > .bmd-layout-header { - margin-top: 0 -} - -:not(.bmd-drawer-out).bmd-drawer-overlay-xl-down.bmd-drawer-f-b > .bmd-layout-content { - margin-bottom: 0 -} - -.ripple { - position: relative -} - -.ripple-container { - position: absolute; - top: 0; - left: 0; - z-index: 1; - width: 100%; - height: 100%; - overflow: hidden; - pointer-events: none; - border-radius: inherit -} - -.ripple-container .ripple-decorator { - position: absolute; - width: 20px; - height: 20px; - margin-top: -10px; - margin-left: -10px; - pointer-events: none; - background-color: rgba(0, 0, 0, .05); - border-radius: 100%; - opacity: 0; - transform: scale(1); - transform-origin: 50% -} - -.ripple-container .ripple-decorator.ripple-on { - opacity: .1; - transition: opacity .15s ease-in 0s, transform .5s cubic-bezier(.4, 0, .2, 1) .1s -} - -.ripple-container .ripple-decorator.ripple-out { - opacity: 0; - transition: opacity .1s linear 0s !important -} - -#snackbar-container { - position: fixed; - bottom: 0; - left: 0; - z-index: 99999; - display: flex; - flex-direction: column; - align-items: center; - width: 100% -} - -.snackbar { - display: inline-block; - min-width: 100%; - max-height: 0; - opacity: 0; - transition: transform .25s cubic-bezier(0, 0, .2, 1), opacity 0s .25s, max-height .25s; - transform: translateY(100%) -} - -.snackbar.toast .snackbar-content { - border-radius: 10rem -} - -.snackbar-content { - display: block; - padding: .8rem 1.5rem; - margin-top: 3px; - font-size: .9rem; - color: #fff; - background-color: #323232; - border-radius: 2px -} - -.snackbar-content .btn, .snackbar-content .custom-file-control:before { - margin-bottom: 0 -} - -.snackbar.snackbar-opened { - max-height: 100px; - opacity: 1; - transition: transform .25s cubic-bezier(0, 0, .2, 1), opacity 0s 0s, max-height .25s; - transform: translateY(0) -} - -@media (min-width: 576px) { - #snackbar-container { - right: 1rem; - left: 1rem; - width: 100% - } - - .snackbar { - min-width: 280px - } -} diff --git a/applications/launchpad/gui-vue/src/assets/oruga-material.css b/applications/launchpad/gui-vue/src/assets/oruga-material.css deleted file mode 100644 index 59d3a7d47f..0000000000 --- a/applications/launchpad/gui-vue/src/assets/oruga-material.css +++ /dev/null @@ -1,99 +0,0 @@ -/* Dropdown */ - -.dropdown-item { - position: relative; - display: flex; - flex-flow: row wrap; - align-items: center; - min-width: 7rem; - min-height: 3rem; - padding: 0.8rem 1rem; - overflow: hidden; - line-height: 1; - text-overflow: ellipsis; - word-wrap: break-word; -} - -/* Buttons */ - -.btn { - height: 100%; -} - -/* Steps */ - -.step-marker { - border-radius: 50%; - height: 50px; - width: 50px; -} - -.step-divider { - background: #b5b5b5; - content: " "; - display: block; - position: absolute; - bottom: 0; - left: -50%; - width: 100%; - height: 5px; - top: 35%; -} - -.steps-nav-item-active .step-marker { - color: var(--primary); - border-color: var(--primary); -} - -.steps-nav-item-active .step-divider { - background: none; - background: var(--primary); -} - -.steps-nav-item-previous .step-marker { - background: var(--primary); -} - -.steps-nav-item-previous .step-divider { - background: none; - background: var(--primary); -} - -/* Checkbox */ - -.form-check { - width: 1.2rem; - height: 1.2rem; - border-color: var(--gray); - background-color: white; -} - -.form-check-checked { - background-color: var(--primary); - border-color: var(--primary); -} - -.form-check-label { - margin-left: 0.2rem; -} - -.form-check:hover:after { - content: ""; - border-radius: 50%; - opacity: 0.2; - cursor: pointer; - height: 34px; - position: absolute; - transition: inherit; - width: 34px; - left: calc(50% - 24px); - top: calc(50% - 24px); - margin: 7px; - background-color: inherit; -} - -/* Field */ - -.field { - margin-top: 0.8rem -} diff --git a/applications/launchpad/gui-vue/src/components/Containers.vue b/applications/launchpad/gui-vue/src/components/Containers.vue deleted file mode 100644 index fc71d05d1d..0000000000 --- a/applications/launchpad/gui-vue/src/components/Containers.vue +++ /dev/null @@ -1,79 +0,0 @@ - - - - - - diff --git a/applications/launchpad/gui-vue/src/components/LaunchDocker.vue b/applications/launchpad/gui-vue/src/components/LaunchDocker.vue deleted file mode 100644 index 57cddad91e..0000000000 --- a/applications/launchpad/gui-vue/src/components/LaunchDocker.vue +++ /dev/null @@ -1,228 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/applications/launchpad/gui-vue/src/components/Logs.vue b/applications/launchpad/gui-vue/src/components/Logs.vue deleted file mode 100644 index cda8d23f1c..0000000000 --- a/applications/launchpad/gui-vue/src/components/Logs.vue +++ /dev/null @@ -1,85 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/applications/launchpad/gui-vue/src/components/Service.vue b/applications/launchpad/gui-vue/src/components/Service.vue deleted file mode 100644 index c92d8ba11e..0000000000 --- a/applications/launchpad/gui-vue/src/components/Service.vue +++ /dev/null @@ -1,116 +0,0 @@ - - - - - - - diff --git a/applications/launchpad/gui-vue/src/components/Settings.vue b/applications/launchpad/gui-vue/src/components/Settings.vue deleted file mode 100644 index 4e66f61845..0000000000 --- a/applications/launchpad/gui-vue/src/components/Settings.vue +++ /dev/null @@ -1,84 +0,0 @@ - - - - - - - diff --git a/applications/launchpad/gui-vue/src/components/oldLauncher.vue b/applications/launchpad/gui-vue/src/components/oldLauncher.vue deleted file mode 100644 index 40634c1621..0000000000 --- a/applications/launchpad/gui-vue/src/components/oldLauncher.vue +++ /dev/null @@ -1,80 +0,0 @@ - - - -
-

Workspace

- - Create workspace - Open workspace -

- Workspace status:{{ workspaceStatus }} -

-
-
-
-

Options

-
-
- - Selected: {{ options.tari_network }} -
-
    -
  • Wait for Tor: seconds
  • -
  • Docker registry:
  • -
  • Docker tag:
  • -
-
- -
-
- - Spin up a Base Node - -
-
- - Spin up a wallet - -
-
-
- -
-
-
- - Spin up a SHA3 miner - -
- -
-
-
- - Spin up a Monero Miner - -
-
-
    -
  • -
  • -
  • -
  • -
    -
  • -
  • -
    -
-
-
- - - -
-Launch! - -
diff --git a/applications/launchpad/gui-vue/src/main.js b/applications/launchpad/gui-vue/src/main.js deleted file mode 100644 index 651930b165..0000000000 --- a/applications/launchpad/gui-vue/src/main.js +++ /dev/null @@ -1,54 +0,0 @@ -// Copyright 2022 The Tari Project -// SPDX-License-Identifier: BSD-3-Clause - -import { createApp } from 'vue' -import App from './App.vue' -import Oruga from '@oruga-ui/oruga-next' -import '@oruga-ui/oruga-next/dist/oruga-full.css' -import { library } from '@fortawesome/fontawesome-svg-core' -import { - faCheck, - faCheckCircle, - faInfoCircle, - faExclamationTriangle, - faExclamationCircle, - faArrowUp, - faAngleRight, - faAngleLeft, - faAngleDown, - faEye, - faEyeSlash, - faCaretDown, - faCaretUp -} from '@fortawesome/free-solid-svg-icons' -import { FontAwesomeIcon } from '@fortawesome/vue-fontawesome' -library.add( - faCheck, - faCheckCircle, - faInfoCircle, - faExclamationTriangle, - faExclamationCircle, - faArrowUp, - faAngleRight, - faAngleLeft, - faAngleDown, - faEye, - faEyeSlash, - faCaretDown, - faCaretUp -); - -import store from "./store"; - -console.log(store); -store.dispatch("initState").then(()=> { - createApp(App) - .use(store) - .component('vue-fontawesome', FontAwesomeIcon) - .use(Oruga, { - iconComponent: 'vue-fontawesome', - iconPack: 'fas' - }) - .mount('#app') -}) - diff --git a/applications/launchpad/gui-vue/src/store.js b/applications/launchpad/gui-vue/src/store.js deleted file mode 100644 index 5e4d972b8f..0000000000 --- a/applications/launchpad/gui-vue/src/store.js +++ /dev/null @@ -1,219 +0,0 @@ -// Copyright 2022 The Tari Project -// SPDX-License-Identifier: BSD-3-Clause - -import {createStore} from 'vuex' -import {invoke} from '@tauri-apps/api/tauri' -import {listen} from "@tauri-apps/api/event"; -import CBuffer from 'CBuffer'; -import {cacheDir, sep} from "@tauri-apps/api/path"; - -async function createDefaultSettings() { - return { - walletPassword: "tari", - moneroMiningAddress: "5AJ8FwQge4UjT9Gbj4zn7yYcnpVQzzkqr636pKto59jQcu85CFsuYVeFgbhUdRpiPjUCkA4sQtWApUzCyTMmSigFG2hDo48", - numMiningThreads: 1, - tariNetwork: "dibbler", - rootFolder: await cacheDir() + "tari" + sep + "tmp" + sep + "dibbler", - dockerRegistry: "quay.io/tarilabs", - dockerTag: "latest", - monerodUrl: "http://stagenet.community.xmr.to:38081,http://monero-stagenet.exan.tech:3808", - moneroUseAuth: false, - moneroUsername: "", - moneroPassword: "" - }; -} - -function handleSystemEvent(commit, payload) { - if (payload.Type === "container") { - return commit('updateContainerStatus', {status: payload.Action, id: payload.Actor.ID}); - } -} - -const store = createStore({ - -state: { - settings: {}, - subscribedToEvents: false, - unsubscribeSystemEvents: () => {}, - containers: {} -}, - mutations: { - init(state, settings) { - // work around to get async settings from tauri - state.settings = settings; - }, - setNetwork(state, network) { - state.settings.tariNetwork = network; - }, - setRootFolder(state, folder) { - state.settings.rootFolder = folder; - }, - setWalletPassword(state, value) { - state.settings.walletPassword = value; - }, - setMoneroMiningAddress(state, value) { - state.settings.moneroMiningAddress = value; - }, - setNumMiningThreads(state, value) { - state.settings.numMiningThreads = value; - }, - setDockerRegistry(state, value) { - state.settings.dockerRegistry = value; - }, - setDockerTag(state, value) { - state.settings.dockerTag = value; - }, - subscribedToEvents(state) { - state.subscribedToEvents = true; - }, - unsubscribeSystemEvents(state, fn) { - state.unsubscribeSystemEvents = fn; - }, - newContainer(state, type) { - if (state.containers[type]) { - console.log(`Container ${type} already exists. Old container to be replaced: `, state.containers[type]); - } - state.containers[type] = { - logs: new CBuffer(1000), - stats: {cpu: 0, mem: 0} - }; - console.log(`Added new container ${type}`); - }, - - startContainer(state, {type, record}) { - if (!state.containers[type]) { - console.log(`Call newContainer before startContainer for ${type}`); - } - state.containers[type].id = record.id; - state.containers[type].listeners = record.listeners; - state.containers[type].logEventsName = record.logEventsName; - state.containers[type].statsEventsName = record.statsEventsName; - state.containers[type].name = record.name; - if (Array.isArray(record.logs)) { - for (let message of record.logs) { - state.containers[type].logs.push(message); - } - } - }, - updateContainerStatus(state, update) { - if (!update.id) { - console.log(`Container status update did not include id`); - return; - } - if (!update.status) { - console.log(`Container status update did not include status`); - return; - } - for (let c in state.containers) { - let container = state.containers[c]; - if (container.id === update.id) { - container.status = update.status; - } - } - }, - updateLog(state, update) { - if (!update.type) { - console.log(`Container status update did not include type`); - return; - } - if (!update.log) { - console.log(`Container status update did not include log message`); - return; - } - state.containers[update.type].logs.push(update.log); - }, - updateContainerStats(state, update) { - console.log("Event received:", update); - if (!update.type) { - console.log(`Container status update did not include type`); - return; - } - if (!update.stats) { - console.log(`Container status update did not include stats`); - return; - } - let cpu = 0; - try { - let cs = update.stats.cpu_stats; - let pcs = update.stats.precpu_stats; - const cpu_delta = cs.cpu_usage.total_usage - pcs.cpu_usage.total_usage; - const system_cpu_delta = cs.system_cpu_usage - pcs.system_cpu_usage; - const numCpu = cs.online_cpus; - cpu = (cpu_delta / system_cpu_delta) * numCpu * 100.0 - } catch { - console.log("Invalid CPU data"); - } - let mem = 0; - try { - let ms = update.stats.memory_stats; - mem = (ms.usage - (ms.stats.cache || 0))/(1024*1024); - } catch { - console.log("Invalid Memory data"); - } - console.log(`${update.type} CPU ${cpu}, Memory: ${mem}`); - state.containers[update.type].stats.cpu = cpu; - state.containers[update.type].stats.mem = mem; - }, - }, - actions: { - async initState({commit}) { - commit("init", await createDefaultSettings()); - }, - async startContainer({state, commit}, type) { - console.log(`Starting container ${type}`); - try { - if (!state.subscribedToEvents) { - console.log("Subscribing to events"); - commit('subscribedToEvents'); - await invoke("events"); - let eventsUnsubscribe = await listen("tari://docker-system-event", (event) => { - console.log("System event: ", event.payload); - handleSystemEvent(commit, event.payload); - }); - commit('unsubscribeSystemEvents', eventsUnsubscribe); - } - - const settings = Object.assign({}, state.settings); - console.log(settings); - let record = await invoke("start_service", {serviceName: type, settings}); - console.log(`Got ${JSON.stringify(record)} as response`) - // Subscribe to stat update events - let logsUnsubscribe = await listen(record.logEventsName, (event) => { - console.log("Log event: ", event.payload); - commit('updateLog', {type, log: event.payload}); - }); - // Subscribe to log events - console.log(`Listening on ${record.statsEventsName}`); - let statsUnsubscribe = await listen(record.statsEventsName, (event) => { - console.log("Stats event: ", event.payload); - commit('updateContainerStats', {type, stats: event.payload}); - }); - record.listeners = [statsUnsubscribe, logsUnsubscribe]; - commit('startContainer', {type, record}); - } catch (err) { - console.log("Error starting service: ", err); - } - }, - async stopContainer({state}, type) { - console.log(`Stopping container ${type}`); - try { - await invoke("stop_service", {serviceName: type, settings: state.settings}); - let service = state.containers[type]; - if (service.listeners) { - if (typeof (service.listeners.statsUnsubscribe) === 'function') { - console.log("Detaching stats listener"); - service.listeners.statsUnsubscribe(); - } - if (typeof (service.listeners.logsUnsubscribe) === 'function') { - console.log("Detaching Log listener"); - service.listeners.logsUnsubscribe(); - } - } - } catch (err) { - console.log("Error starting service: ", err); - } - } - } -}) - -export default store diff --git a/applications/launchpad/package.json b/applications/launchpad/package.json deleted file mode 100644 index 6c39f7def6..0000000000 --- a/applications/launchpad/package.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "scripts": { - "tauri": "tauri" - }, - "devDependencies": { - "@tauri-apps/cli": "^1.0.0-rc.8" - } -} diff --git a/applications/launchpad/versions.txt b/applications/launchpad/versions.txt deleted file mode 100644 index 6d18c165c2..0000000000 --- a/applications/launchpad/versions.txt +++ /dev/null @@ -1,3 +0,0 @@ -# Version refers to the base_node, wallet, etc. version -VERSION=0.31.1-lp2 -XMRIG_VERSION=v6.16.3 \ No newline at end of file diff --git a/applications/tari_app_grpc/proto/wallet.proto b/applications/tari_app_grpc/proto/wallet.proto index 8b2832f1db..eb0719d5d3 100644 --- a/applications/tari_app_grpc/proto/wallet.proto +++ b/applications/tari_app_grpc/proto/wallet.proto @@ -83,6 +83,10 @@ service Wallet { rpc SetBaseNode(SetBaseNodeRequest) returns (SetBaseNodeResponse); + rpc StreamTransactionEvents(TransactionEventRequest) returns (stream TransactionEventResponse); + rpc SeedWords(Empty) returns (SeedWordsResponse); + rpc DeleteSeedWordsFile(Empty) returns (FileDeletedResponse); + rpc SubmitContractAcceptance(SubmitContractAcceptanceRequest) returns (SubmitContractAcceptanceResponse); rpc SubmitContractUpdateProposalAcceptance(SubmitContractUpdateProposalAcceptanceRequest) returns (SubmitContractUpdateProposalAcceptanceResponse); } @@ -395,3 +399,31 @@ message CheckConnectivityResponse{ } OnlineStatus status = 1; } + +message TransactionEventRequest{ + +} + +message TransactionEvent { + string event = 1; + string tx_id = 2; + bytes source_pk = 3; + bytes dest_pk = 4; + string status = 5; + string direction = 6; + uint64 amount = 7; + string message = 8; + bool is_coinbase = 9; +} + +message TransactionEventResponse { + TransactionEvent transaction = 1; +} + +message SeedWordsResponse { + repeated string words = 1; +} + +message FileDeletedResponse { + +} \ No newline at end of file diff --git a/applications/tari_console_wallet/src/automation/commands.rs b/applications/tari_console_wallet/src/automation/commands.rs index d6ae1c2b8c..801b223cc8 100644 --- a/applications/tari_console_wallet/src/automation/commands.rs +++ b/applications/tari_console_wallet/src/automation/commands.rs @@ -252,7 +252,7 @@ pub async fn coin_split( transaction_service: &mut TransactionServiceHandle, ) -> Result { let (tx_id, tx, amount) = output_service - .create_coin_split(amount_per_split, num_splits as usize, fee_per_gram, None) + .create_coin_split(vec![], amount_per_split, num_splits as usize, fee_per_gram) .await?; transaction_service .submit_transaction(tx_id, tx, amount, message) diff --git a/applications/tari_console_wallet/src/grpc/mod.rs b/applications/tari_console_wallet/src/grpc/mod.rs index 4a9332a4b1..3109843d26 100644 --- a/applications/tari_console_wallet/src/grpc/mod.rs +++ b/applications/tari_console_wallet/src/grpc/mod.rs @@ -3,4 +3,63 @@ mod wallet_grpc_server; +use tari_app_grpc::tari_rpc::TransactionEvent; +use tari_utilities::hex::Hex; +use tari_wallet::transaction_service::storage::models::{ + CompletedTransaction, + InboundTransaction, + OutboundTransaction, +}; + pub use self::wallet_grpc_server::*; + +pub enum TransactionWrapper { + Completed(CompletedTransaction), + Outbound(OutboundTransaction), + Inbound(InboundTransaction), +} + +pub fn convert_to_transaction_event(event: String, source: TransactionWrapper) -> TransactionEvent { + match source { + TransactionWrapper::Completed(completed) => TransactionEvent { + event, + tx_id: completed.tx_id.to_string(), + source_pk: completed.source_public_key.to_hex().into_bytes(), + dest_pk: completed.destination_public_key.to_hex().into_bytes(), + status: completed.status.to_string(), + direction: completed.direction.to_string(), + amount: completed.amount.as_u64(), + message: completed.message.to_string(), + is_coinbase: completed.is_coinbase(), + }, + TransactionWrapper::Outbound(outbound) => TransactionEvent { + event, + tx_id: outbound.tx_id.to_string(), + source_pk: vec![], + dest_pk: outbound.destination_public_key.to_hex().into_bytes(), + status: outbound.status.to_string(), + direction: "outbound".to_string(), + amount: outbound.amount.as_u64(), + message: outbound.message, + is_coinbase: false, + }, + TransactionWrapper::Inbound(inbound) => TransactionEvent { + event, + tx_id: inbound.tx_id.to_string(), + source_pk: inbound.source_public_key.to_hex().into_bytes(), + dest_pk: vec![], + status: inbound.status.to_string(), + direction: "inbound".to_string(), + amount: inbound.amount.as_u64(), + message: inbound.message.clone(), + /// The coinbase are technically Inbound. + /// To determine whether a transaction is coinbase + /// we will check whether the message contains `Coinbase`. + is_coinbase: if inbound.message.to_lowercase().contains("coinbase") { + true + } else { + false + }, + }, + } +} diff --git a/applications/tari_console_wallet/src/grpc/wallet_grpc_server.rs b/applications/tari_console_wallet/src/grpc/wallet_grpc_server.rs index b2463cba5e..1d8e9d9465 100644 --- a/applications/tari_console_wallet/src/grpc/wallet_grpc_server.rs +++ b/applications/tari_console_wallet/src/grpc/wallet_grpc_server.rs @@ -20,9 +20,17 @@ // WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE // USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -use std::convert::{TryFrom, TryInto}; +use std::{ + convert::{TryFrom, TryInto}, + fs, +}; -use futures::{channel::mpsc, future, SinkExt}; +use clap::Parser; +use futures::{ + channel::mpsc::{self, Sender}, + future, + SinkExt, +}; use log::*; use tari_app_grpc::{ conversions::naive_datetime_to_timestamp, @@ -43,6 +51,7 @@ use tari_app_grpc::{ CreateFollowOnAssetCheckpointResponse, CreateInitialAssetCheckpointRequest, CreateInitialAssetCheckpointResponse, + FileDeletedResponse, GetBalanceRequest, GetBalanceResponse, GetCoinbaseRequest, @@ -66,6 +75,7 @@ use tari_app_grpc::{ RegisterAssetResponse, RevalidateRequest, RevalidateResponse, + SeedWordsResponse, SendShaAtomicSwapRequest, SendShaAtomicSwapResponse, SetBaseNodeRequest, @@ -75,6 +85,9 @@ use tari_app_grpc::{ SubmitContractUpdateProposalAcceptanceRequest, SubmitContractUpdateProposalAcceptanceResponse, TransactionDirection, + TransactionEvent, + TransactionEventRequest, + TransactionEventResponse, TransactionInfo, TransactionStatus, TransferRequest, @@ -82,7 +95,11 @@ use tari_app_grpc::{ TransferResult, }, }; -use tari_common_types::types::{BlockHash, FixedHash, PublicKey, Signature}; +use tari_common_types::{ + array::copy_into_fixed_array, + transaction::TxId, + types::{BlockHash, PublicKey, Signature, FixedHash}, +}; use tari_comms::{multiaddr::Multiaddr, types::CommsPublicKey, CommsNode}; use tari_core::transactions::{ tari_amount::MicroTari, @@ -92,14 +109,38 @@ use tari_utilities::{hex::Hex, ByteArray, Hashable}; use tari_wallet::{ connectivity_service::{OnlineStatus, WalletConnectivityInterface}, output_manager_service::handle::OutputManagerHandle, - transaction_service::{handle::TransactionServiceHandle, storage::models}, + transaction_service::{ + handle::TransactionServiceHandle, + storage::models::{self, WalletTransaction}, + }, WalletSqlite, }; -use tokio::task; +use tokio::{sync::broadcast, task}; use tonic::{Request, Response, Status}; +use crate::{ + cli::Cli, + grpc::{convert_to_transaction_event, TransactionWrapper}, + notifier::{CANCELLED, CONFIRMATION, MINED, NEW_BLOCK_MINED, QUEUED, RECEIVED, SENT}, +}; + const LOG_TARGET: &str = "wallet::ui::grpc"; +async fn send_transaction_event( + transaction_event: TransactionEvent, + sender: &mut Sender>, +) { + let response = TransactionEventResponse { + transaction: Some(transaction_event), + }; + if let Err(err) = sender.send(Ok(response)).await { + warn!(target: LOG_TARGET, "Error sending transaction via GRPC: {}", err); + if let Err(send_err) = sender.send(Err(Status::unknown("Error sending data"))).await { + warn!(target: LOG_TARGET, "Error sending error to GRPC client: {}", send_err) + } + } +} + pub struct WalletGrpcServer { wallet: WalletSqlite, } @@ -125,6 +166,7 @@ impl WalletGrpcServer { #[tonic::async_trait] impl wallet_server::Wallet for WalletGrpcServer { type GetCompletedTransactionsStream = mpsc::Receiver>; + type StreamTransactionEventsStream = mpsc::Receiver>; async fn get_version(&self, _: Request) -> Result, Status> { Ok(Response::new(GetVersionResponse { @@ -553,6 +595,85 @@ impl wallet_server::Wallet for WalletGrpcServer { Ok(Response::new(GetTransactionInfoResponse { transactions })) } + async fn stream_transaction_events( + &self, + _request: tonic::Request, + ) -> Result, Status> { + let (mut sender, receiver) = mpsc::channel(100); + + // let event_listener = self.events_channel.clone(); + + // let mut shutdown_signal = self.wallet; + let mut transaction_service = self.wallet.transaction_service.clone(); + let mut transaction_service_events = self.wallet.transaction_service.get_event_stream(); + + task::spawn(async move { + loop { + tokio::select! { + result = transaction_service_events.recv() => { + match result { + Ok(msg) => { + use tari_wallet::transaction_service::handle::TransactionEvent::*; + match (*msg).clone() { + NewBlockMined(tx_id) => { + match transaction_service.get_any_transaction(tx_id).await{ + Ok(found_transaction) => { + if let Some(WalletTransaction::PendingOutbound(tx)) = found_transaction { + let transaction_event = convert_to_transaction_event(NEW_BLOCK_MINED.to_string(), + TransactionWrapper::Outbound(tx.clone())); + send_transaction_event(transaction_event, &mut sender).await; + } + + }, + Err(e) => error!(target: LOG_TARGET, "Transaction service error: {}", e), + } + }, + ReceivedFinalizedTransaction(tx_id) => handle_completed_tx(tx_id, RECEIVED, &mut transaction_service, &mut sender).await, + TransactionMinedUnconfirmed{tx_id, num_confirmations: _, is_valid: _} | FauxTransactionUnconfirmed{tx_id, num_confirmations: _, is_valid: _}=> handle_completed_tx(tx_id, CONFIRMATION, &mut transaction_service, &mut sender).await, + TransactionMined{tx_id, is_valid: _} | FauxTransactionConfirmed{tx_id, is_valid: _} => handle_completed_tx(tx_id, MINED, &mut transaction_service, &mut sender).await, + TransactionCancelled(tx_id, _) => { + match transaction_service.get_any_transaction(tx_id).await{ + Ok(Some(wallet_tx)) => { + use WalletTransaction::*; + let transaction_event = match wallet_tx { + Completed(tx) => convert_to_transaction_event(CANCELLED.to_string(), TransactionWrapper::Completed(tx)), + PendingInbound(tx) => convert_to_transaction_event(CANCELLED.to_string(), TransactionWrapper::Inbound(tx)), + PendingOutbound(tx) => convert_to_transaction_event(CANCELLED.to_string(), TransactionWrapper::Outbound(tx)), + }; + send_transaction_event(transaction_event, &mut sender).await; + }, + Err(e) => error!(target: LOG_TARGET, "Transaction service error: {}", e), + _ => error!(target: LOG_TARGET, "Transaction not found tx_id: {}", tx_id), + } + }, + TransactionCompletedImmediately(tx_id) => handle_pending_outbound(tx_id, SENT, &mut transaction_service, &mut sender).await, + TransactionSendResult(tx_id, status) => { + let is_sent = status.direct_send_result || status.store_and_forward_send_result; + let event = if is_sent { SENT } else { QUEUED }; + handle_pending_outbound(tx_id, event, &mut transaction_service, &mut sender).await; + }, + TransactionValidationStateChanged(_t_operation_id) => { + send_transaction_event(simple_event("unknown"), &mut sender).await; + }, + ReceivedTransaction(_) | ReceivedTransactionReply(_) | TransactionBroadcast(_) | TransactionMinedRequestTimedOut(_) | TransactionImported(_) => { + send_transaction_event(simple_event("not_supported"), &mut sender).await; + }, + // Only the above variants trigger state refresh + _ => (), + } + }, + Err(broadcast::error::RecvError::Lagged(n)) => { + warn!(target: LOG_TARGET, "Missed {} from Transaction events", n); + } + Err(broadcast::error::RecvError::Closed) => {} + } + } + } + } + }); + Ok(Response::new(receiver)) + } + async fn get_completed_transactions( &self, _request: Request, @@ -612,21 +733,15 @@ impl wallet_server::Wallet for WalletGrpcServer { async fn coin_split(&self, request: Request) -> Result, Status> { let message = request.into_inner(); - let lock_height = if message.lock_height == 0 { - None - } else { - Some(message.lock_height) - }; - let mut wallet = self.wallet.clone(); let tx_id = wallet .coin_split( + vec![], // TODO: refactor grpc to accept and use commitments MicroTari::from(message.amount_per_split), message.split_count as usize, MicroTari::from(message.fee_per_gram), message.message, - lock_height, ) .await .map_err(|e| Status::internal(format!("{:?}", e)))?; @@ -1090,6 +1205,100 @@ impl wallet_server::Wallet for WalletGrpcServer { }, } } + + async fn seed_words(&self, _: Request) -> Result, Status> { + let cli = Cli::parse(); + let file_path = cli.seed_words_file_name.unwrap(); + + if !file_path.is_file() { + return Err(Status::not_found("file not found")); + } + + let file_name = file_path.clone().into_os_string().into_string().unwrap(); + + if file_name.is_empty() { + return Err(Status::not_found("seed_words_file_name is empty")); + } + + let contents = fs::read_to_string(file_path)?; + let words = contents + .split(" ") + .collect::>() + .iter() + .map(|&x| x.into()) + .collect::>(); + Ok(Response::new(SeedWordsResponse { words })) + } + + async fn delete_seed_words_file( + &self, + _: Request, + ) -> Result, Status> { + let cli = Cli::parse(); + let file_path = cli.seed_words_file_name.unwrap(); + + if !file_path.is_file() { + return Err(Status::not_found("file not found")); + } + + let file_name = file_path.clone().into_os_string().into_string().unwrap(); + + if file_name.is_empty() { + return Err(Status::not_found("seed_words_file_name is empty")); + } + fs::remove_file(file_path)?; + Ok(Response::new(FileDeletedResponse {})) + } +} + +async fn handle_completed_tx( + tx_id: TxId, + event: &str, + transaction_service: &mut TransactionServiceHandle, + sender: &mut Sender>, +) { + match transaction_service.get_completed_transaction(tx_id).await { + Ok(completed) => { + let transaction_event = + convert_to_transaction_event(event.to_string(), TransactionWrapper::Completed(completed)); + send_transaction_event(transaction_event, sender).await; + }, + Err(e) => error!(target: LOG_TARGET, "Transaction service error: {}", e), + } +} + +async fn handle_pending_outbound( + tx_id: TxId, + event: &str, + transaction_service: &mut TransactionServiceHandle, + sender: &mut Sender>, +) { + match transaction_service.get_pending_outbound_transactions().await { + Ok(txs) => { + if let Some(tx) = txs.get(&tx_id) { + let transaction_event = + convert_to_transaction_event(event.to_string(), TransactionWrapper::Outbound(tx.clone())); + send_transaction_event(transaction_event, sender).await; + } else { + error!(target: LOG_TARGET, "Not found in pending outbound set tx_id: {}", tx_id); + } + }, + Err(e) => error!(target: LOG_TARGET, "Transaction service error: {}", e), + } +} + +fn simple_event(event: &str) -> TransactionEvent { + TransactionEvent { + event: event.to_string(), + tx_id: String::default(), + source_pk: vec![], + dest_pk: vec![], + status: event.to_string(), + direction: event.to_string(), + amount: 0, + message: String::default(), + is_coinbase: false, + } } fn convert_wallet_transaction_into_transaction_info( diff --git a/applications/tari_console_wallet/src/main.rs b/applications/tari_console_wallet/src/main.rs index 6e2a10617a..b679fa33ec 100644 --- a/applications/tari_console_wallet/src/main.rs +++ b/applications/tari_console_wallet/src/main.rs @@ -65,7 +65,6 @@ mod ui; mod utils; mod wallet_modes; -/// Application entry point fn main() { // Uncomment to enable tokio tracing via tokio-console // console_subscriber::init(); diff --git a/applications/tari_console_wallet/src/notifier/mod.rs b/applications/tari_console_wallet/src/notifier/mod.rs index 5f4ce8162b..224cd188a5 100644 --- a/applications/tari_console_wallet/src/notifier/mod.rs +++ b/applications/tari_console_wallet/src/notifier/mod.rs @@ -38,26 +38,52 @@ use tari_wallet::{ }, WalletSqlite, }; -use tokio::runtime::Handle; - +use tokio::{runtime::Handle, sync::broadcast::Sender}; pub const LOG_TARGET: &str = "wallet::notifier"; -const RECEIVED: &str = "received"; -const SENT: &str = "sent"; -const QUEUED: &str = "queued"; -const CONFIRMATION: &str = "confirmation"; -const MINED: &str = "mined"; -const CANCELLED: &str = "cancelled"; +pub const RECEIVED: &str = "received"; +pub const SENT: &str = "sent"; +pub const QUEUED: &str = "queued"; +pub const CONFIRMATION: &str = "confirmation"; +pub const MINED: &str = "mined"; +pub const CANCELLED: &str = "cancelled"; +pub const NEW_BLOCK_MINED: &str = "new_block_mined"; +#[derive(Clone)] +pub enum WalletEventMessage { + Completed { + event: String, + transaction: CompletedTransaction, + }, + Outbound { + event: String, + transaction: OutboundTransaction, + }, + Inbound { + event: String, + transaction: InboundTransaction, + }, +} #[derive(Clone)] pub struct Notifier { path: Option, handle: Handle, wallet: WalletSqlite, + event_broadcaster: Sender, } impl Notifier { - pub fn new(path: Option, handle: Handle, wallet: WalletSqlite) -> Self { - Self { path, handle, wallet } + pub fn new( + path: Option, + handle: Handle, + wallet: WalletSqlite, + event_broadcaster: Sender, + ) -> Self { + Self { + path, + handle, + wallet, + event_broadcaster, + } } /// Trigger a notification that a negotiated transaction was received. @@ -66,12 +92,16 @@ impl Notifier { if let Some(program) = self.path.clone() { let mut transaction_service = self.wallet.transaction_service.clone(); - + let sender = self.event_broadcaster.clone(); self.handle.spawn(async move { match transaction_service.get_completed_transaction(tx_id).await { Ok(tx) => { let args = args_from_complete(&tx, RECEIVED, None); let result = Command::new(program).args(&args).output(); + let _ignored = sender.send(WalletEventMessage::Completed { + event: RECEIVED.to_string(), + transaction: tx, + }); log(result); }, Err(e) => error!(target: LOG_TARGET, "Transaction service error: {}", e), @@ -88,12 +118,17 @@ impl Notifier { if let Some(program) = self.path.clone() { let mut transaction_service = self.wallet.transaction_service.clone(); - + let sender = self.event_broadcaster.clone(); self.handle.spawn(async move { match transaction_service.get_completed_transaction(tx_id).await { Ok(tx) => { let args = args_from_complete(&tx, CONFIRMATION, Some(confirmations)); let result = Command::new(program).args(&args).output(); + let message = WalletEventMessage::Completed { + event: CONFIRMATION.to_string(), + transaction: tx, + }; + let _ignored = sender.send(message); log(result); }, Err(e) => error!(target: LOG_TARGET, "Transaction service error: {}", e), @@ -110,7 +145,7 @@ impl Notifier { if let Some(program) = self.path.clone() { let mut transaction_service = self.wallet.transaction_service.clone(); - + let sender = self.event_broadcaster.clone(); self.handle.spawn(async move { match transaction_service.get_completed_transaction(tx_id).await { Ok(tx) => { @@ -121,8 +156,14 @@ impl Notifier { None }, }; + let args = args_from_complete(&tx, MINED, confirmations); let result = Command::new(program).args(&args).output(); + let message = WalletEventMessage::Completed { + event: MINED.to_string(), + transaction: tx, + }; + let _ignored = sender.send(message); log(result); }, Err(e) => error!(target: LOG_TARGET, "Transaction service error: {}", e), @@ -148,13 +189,19 @@ impl Notifier { if let Some(program) = self.path.clone() { let mut transaction_service = self.wallet.transaction_service.clone(); - + let sender = self.event_broadcaster.clone(); self.handle.spawn(async move { match transaction_service.get_pending_outbound_transactions().await { Ok(txs) => { if let Some(tx) = txs.get(&tx_id) { let args = args_from_outbound(tx, event); let result = Command::new(program).args(&args).output(); + let message = WalletEventMessage::Outbound { + event: event.to_string(), + transaction: tx.clone(), + }; + let _ignored = sender.send(message); + log(result); } else { error!(target: LOG_TARGET, "Not found in pending outbound set tx_id: {}", tx_id); @@ -174,15 +221,37 @@ impl Notifier { if let Some(program) = self.path.clone() { let mut transaction_service = self.wallet.transaction_service.clone(); - + let sender = self.event_broadcaster.clone(); self.handle.spawn(async move { match transaction_service.get_any_transaction(tx_id).await { Ok(Some(wallet_tx)) => { let args = match wallet_tx { - WalletTransaction::Completed(tx) => args_from_complete(&tx, CANCELLED, None), - WalletTransaction::PendingInbound(tx) => args_from_inbound(&tx, CANCELLED), - WalletTransaction::PendingOutbound(tx) => args_from_outbound(&tx, CANCELLED), + WalletTransaction::Completed(tx) => { + let message = WalletEventMessage::Completed { + event: CANCELLED.to_string(), + transaction: tx.clone(), + }; + let _ignored = sender.send(message); + args_from_complete(&tx, CANCELLED, None) + }, + WalletTransaction::PendingInbound(tx) => { + let message = WalletEventMessage::Inbound { + event: CANCELLED.to_string(), + transaction: tx.clone(), + }; + let _ignored = sender.send(message); + args_from_inbound(&tx, CANCELLED) + }, + WalletTransaction::PendingOutbound(tx) => { + let message = WalletEventMessage::Outbound { + event: CANCELLED.to_string(), + transaction: tx.clone(), + }; + let _ignored = sender.send(message); + args_from_outbound(&tx, CANCELLED) + }, }; + // c let result = Command::new(program).args(&args).output(); log(result); }, diff --git a/applications/tari_console_wallet/src/wallet_modes.rs b/applications/tari_console_wallet/src/wallet_modes.rs index 2996fc1060..8201adace5 100644 --- a/applications/tari_console_wallet/src/wallet_modes.rs +++ b/applications/tari_console_wallet/src/wallet_modes.rs @@ -19,6 +19,7 @@ // SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, // WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE // USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + use std::{io::Stdout, path::PathBuf}; use log::*; @@ -26,7 +27,7 @@ use rand::{rngs::OsRng, seq::SliceRandom}; use tari_common::exit_codes::{ExitCode, ExitError}; use tari_comms::{multiaddr::Multiaddr, peer_manager::Peer, utils::multiaddr::multiaddr_to_socketaddr}; use tari_wallet::{WalletConfig, WalletSqlite}; -use tokio::runtime::Handle; +use tokio::{runtime::Handle, sync::broadcast}; use tonic::transport::Server; use tui::backend::CrosstermBackend; @@ -40,7 +41,6 @@ use crate::{ ui::App, utils::db::get_custom_base_node_peer_from_db, }; - pub const LOG_TARGET: &str = "wallet::app::main"; #[derive(Debug, Clone)] @@ -244,12 +244,18 @@ pub fn tui_mode( base_node_config: &PeerConfig, mut wallet: WalletSqlite, ) -> Result<(), ExitError> { + let (events_broadcaster, _events_listener) = broadcast::channel(100); if let Some(ref grpc_address) = config.grpc_address { let grpc = WalletGrpcServer::new(wallet.clone()); handle.spawn(run_grpc(grpc, grpc_address.clone())); } - let notifier = Notifier::new(config.notify_file.clone(), handle.clone(), wallet.clone()); + let notifier = Notifier::new( + config.notify_file.clone(), + handle.clone(), + wallet.clone(), + events_broadcaster, + ); let base_node_selected; if let Some(peer) = base_node_config.base_node_custom.clone() { diff --git a/base_layer/common_types/src/chain_metadata.rs b/base_layer/common_types/src/chain_metadata.rs index 817f7a9862..9f78dce4e7 100644 --- a/base_layer/common_types/src/chain_metadata.rs +++ b/base_layer/common_types/src/chain_metadata.rs @@ -44,6 +44,8 @@ pub struct ChainMetadata { pruned_height: u64, /// The total accumulated proof of work of the longest chain accumulated_difficulty: u128, + /// Timestamp of the tip block in the longest valid chain + timestamp: u64, } impl ChainMetadata { @@ -53,6 +55,7 @@ impl ChainMetadata { pruning_horizon: u64, pruned_height: u64, accumulated_difficulty: u128, + timestamp: u64, ) -> ChainMetadata { ChainMetadata { height_of_longest_chain: height, @@ -60,6 +63,7 @@ impl ChainMetadata { pruning_horizon, pruned_height, accumulated_difficulty, + timestamp, } } @@ -70,6 +74,7 @@ impl ChainMetadata { pruning_horizon: 0, pruned_height: 0, accumulated_difficulty: 0, + timestamp: 0, } } @@ -130,6 +135,10 @@ impl ChainMetadata { pub fn best_block(&self) -> &BlockHash { &self.best_block } + + pub fn timestamp(&self) -> u64 { + self.timestamp + } } impl Display for ChainMetadata { diff --git a/base_layer/core/src/base_node/chain_metadata_service/service.rs b/base_layer/core/src/base_node/chain_metadata_service/service.rs index eb60aa2624..4bf454f318 100644 --- a/base_layer/core/src/base_node/chain_metadata_service/service.rs +++ b/base_layer/core/src/base_node/chain_metadata_service/service.rs @@ -313,6 +313,7 @@ mod test { best_block: Some(vec![]), pruned_height: 0, accumulated_difficulty: diff.to_be_bytes().to_vec(), + timestamp: Some(0), } } diff --git a/base_layer/core/src/base_node/proto/chain_metadata.proto b/base_layer/core/src/base_node/proto/chain_metadata.proto index 8a7302f2fe..6acde5f455 100644 --- a/base_layer/core/src/base_node/proto/chain_metadata.proto +++ b/base_layer/core/src/base_node/proto/chain_metadata.proto @@ -19,4 +19,6 @@ message ChainMetadata { // If `pruned_height` is equal to the `height_of_longest_chain` no blocks can be provided. // Archival nodes wil always have an `pruned_height` of zero. uint64 pruned_height = 6; + // Timestamp of the last block in the chain, or `None` if there is no chain + google.protobuf.UInt64Value timestamp = 7; } diff --git a/base_layer/core/src/base_node/proto/chain_metadata.rs b/base_layer/core/src/base_node/proto/chain_metadata.rs index 0e3f567ed4..70dd73c6f4 100644 --- a/base_layer/core/src/base_node/proto/chain_metadata.rs +++ b/base_layer/core/src/base_node/proto/chain_metadata.rs @@ -57,6 +57,7 @@ impl TryFrom for ChainMetadata { pruning_horizon, metadata.pruned_height, accumulated_difficulty, + metadata.timestamp.unwrap_or_default(), )) } } @@ -69,6 +70,7 @@ impl From for proto::ChainMetadata { best_block: Some(metadata.best_block().clone()), pruned_height: metadata.pruned_height(), accumulated_difficulty, + timestamp: Some(metadata.timestamp()), } } } diff --git a/base_layer/core/src/base_node/proto/rpc.proto b/base_layer/core/src/base_node/proto/rpc.proto index 77dccc86e1..f1d9095ac6 100644 --- a/base_layer/core/src/base_node/proto/rpc.proto +++ b/base_layer/core/src/base_node/proto/rpc.proto @@ -26,21 +26,28 @@ message BlockBodyResponse { message SyncHeadersRequest { // Start sending from this hash (exclusive) bytes start_hash = 1; - // Number of blocks to send. If this is zero (empty) the peer SHOULD send to their tip height + // Number of blocks to send. If this is zero (empty) the peer SHOULD send to + // their tip height uint64 count = 2; } // Find at which point the chain splits. message FindChainSplitRequest { - // A set of block hashes ordered in height descending order from the chain tip. + // A set of block hashes ordered in height descending order from the chain + // tip. repeated bytes block_hashes = 1; - // The maximum number of headers to return starting at the first header after the matched height - uint64 header_count = 2;} + // The maximum number of headers to return starting at the first header after + // the matched height + uint64 header_count = 2; +} message FindChainSplitResponse { - // An ordered list of headers starting from next header after the matching hash, up until `FindChainSplitRequest::count` + // An ordered list of headers starting from next header after the matching + // hash, up until `FindChainSplitRequest::count` repeated tari.core.BlockHeader headers = 1; - // The index of the hash that matched from `FindChainSplitRequest::block_hashes`. This value could also be used to know how far back a split occurs. + // The index of the hash that matched from + // `FindChainSplitRequest::block_hashes`. This value could also be used to + // know how far back a split occurs. uint64 fork_hash_index = 2; /// The current header height of this node uint64 tip_height = 3; @@ -51,13 +58,13 @@ message SyncKernelsRequest { bytes end_header_hash = 2; } -message SyncUtxosRequest { +message SyncUtxosRequest { uint64 start = 1; bytes end_header_hash = 2; bool include_pruned_utxos = 3; bool include_deleted_bitmaps = 4; } -message SyncUtxosResponse { +message SyncUtxosResponse { oneof utxo_or_deleted { SyncUtxo utxo = 1; bytes deleted_diff = 2; @@ -79,15 +86,16 @@ message PrunedOutput { bytes witness_hash = 2; } -message SyncUtxosByBlockRequest { +message SyncUtxosByBlockRequest { bytes start_header_hash = 1; bytes end_header_hash = 2; } -message SyncUtxosByBlockResponse { +message SyncUtxosByBlockResponse { repeated tari.types.TransactionOutput outputs = 1; uint64 height = 2; bytes header_hash = 3; + uint64 mined_timestamp = 4; } message GetMempoolFeePerGramStatsRequest { diff --git a/base_layer/core/src/base_node/proto/wallet_rpc.proto b/base_layer/core/src/base_node/proto/wallet_rpc.proto index 5abd408e85..889e4b6683 100644 --- a/base_layer/core/src/base_node/proto/wallet_rpc.proto +++ b/base_layer/core/src/base_node/proto/wallet_rpc.proto @@ -11,97 +11,97 @@ import "transaction.proto"; package tari.base_node; enum TxSubmissionRejectionReason { - TxSubmissionRejectionReasonNone = 0; - TxSubmissionRejectionReasonAlreadyMined = 1; - TxSubmissionRejectionReasonDoubleSpend = 2; - TxSubmissionRejectionReasonOrphan = 3; - TxSubmissionRejectionReasonTimeLocked = 4; - TxSubmissionRejectionReasonValidationFailed = 5; + TxSubmissionRejectionReasonNone = 0; + TxSubmissionRejectionReasonAlreadyMined = 1; + TxSubmissionRejectionReasonDoubleSpend = 2; + TxSubmissionRejectionReasonOrphan = 3; + TxSubmissionRejectionReasonTimeLocked = 4; + TxSubmissionRejectionReasonValidationFailed = 5; } message TxSubmissionResponse { - bool accepted = 1; - TxSubmissionRejectionReason rejection_reason = 2; - bool is_synced = 3; + bool accepted = 1; + TxSubmissionRejectionReason rejection_reason = 2; + bool is_synced = 3; } enum TxLocation { - TxLocationNone = 0; - TxLocationNotStored = 1; - TxLocationInMempool = 2; - TxLocationMined = 3; + TxLocationNone = 0; + TxLocationNotStored = 1; + TxLocationInMempool = 2; + TxLocationMined = 3; } message TxQueryResponse { - TxLocation location = 1; - google.protobuf.BytesValue block_hash = 2; - uint64 confirmations = 3; - bool is_synced = 4; - uint64 height_of_longest_chain = 5; + TxLocation location = 1; + google.protobuf.BytesValue block_hash = 2; + uint64 confirmations = 3; + bool is_synced = 4; + uint64 height_of_longest_chain = 5; + google.protobuf.UInt64Value mined_timestamp = 6; } message TxQueryBatchResponse { - tari.types.Signature signature = 1; - TxLocation location = 2; - google.protobuf.BytesValue block_hash = 3; - uint64 confirmations = 4; - uint64 block_height = 5; + tari.types.Signature signature = 1; + TxLocation location = 2; + google.protobuf.BytesValue block_hash = 3; + uint64 confirmations = 4; + uint64 block_height = 5; + google.protobuf.UInt64Value mined_timestamp = 6; } message TxQueryBatchResponses { - repeated TxQueryBatchResponse responses = 1; - bool is_synced = 2; - google.protobuf.BytesValue tip_hash = 3; - uint64 height_of_longest_chain = 4; - + repeated TxQueryBatchResponse responses = 1; + bool is_synced = 2; + google.protobuf.BytesValue tip_hash = 3; + uint64 height_of_longest_chain = 4; + google.protobuf.UInt64Value tip_mined_timestamp = 5; } message FetchMatchingUtxos { - repeated bytes output_hashes = 1; + repeated bytes output_hashes = 1; } message FetchUtxosResponse { - repeated tari.types.TransactionOutput outputs = 1; - bool is_synced = 2; + repeated tari.types.TransactionOutput outputs = 1; + bool is_synced = 2; } - -message QueryDeletedRequest{ - repeated uint64 mmr_positions = 1; - google.protobuf.BytesValue chain_must_include_header = 2; - bool include_deleted_block_data = 3; +message QueryDeletedRequest { + repeated uint64 mmr_positions = 1; + google.protobuf.BytesValue chain_must_include_header = 2; + bool include_deleted_block_data = 3; } message QueryDeletedResponse { - repeated uint64 deleted_positions = 1; - repeated uint64 not_deleted_positions = 2; - bytes best_block = 3; - uint64 height_of_longest_chain = 4; - repeated bytes blocks_deleted_in = 5; - repeated uint64 heights_deleted_at = 6; + repeated uint64 deleted_positions = 1; + repeated uint64 not_deleted_positions = 2; + bytes best_block = 3; + uint64 height_of_longest_chain = 4; + repeated bytes blocks_deleted_in = 5; + repeated uint64 heights_deleted_at = 6; } -message UtxoQueryRequest{ - repeated bytes output_hashes =1; +message UtxoQueryRequest { + repeated bytes output_hashes = 1; } message UtxoQueryResponses { - repeated UtxoQueryResponse responses =1; - bytes best_block = 3; - uint64 height_of_longest_chain = 4; + repeated UtxoQueryResponse responses = 1; + bytes best_block = 3; + uint64 height_of_longest_chain = 4; } message UtxoQueryResponse { - tari.types.TransactionOutput output = 1; - uint64 mmr_position = 2; - uint64 mined_height =3; - bytes mined_in_block = 4; - bytes output_hash = 5; - + tari.types.TransactionOutput output = 1; + uint64 mmr_position = 2; + uint64 mined_height = 3; + bytes mined_in_block = 4; + bytes output_hash = 5; + uint64 mined_timestamp = 6; } message TipInfoResponse { - ChainMetadata metadata = 1; - bool is_synced = 2; + ChainMetadata metadata = 1; + bool is_synced = 2; } - diff --git a/base_layer/core/src/base_node/proto/wallet_rpc.rs b/base_layer/core/src/base_node/proto/wallet_rpc.rs index b8ae8d66fc..fd5ea20054 100644 --- a/base_layer/core/src/base_node/proto/wallet_rpc.rs +++ b/base_layer/core/src/base_node/proto/wallet_rpc.rs @@ -127,6 +127,7 @@ pub struct TxQueryResponse { pub confirmations: u64, pub is_synced: bool, pub height_of_longest_chain: u64, + pub mined_timestamp: Option, } #[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] @@ -136,6 +137,7 @@ pub struct TxQueryBatchResponse { pub block_hash: Option, pub confirmations: u64, pub block_height: u64, + pub mined_timestamp: Option, } #[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] @@ -194,6 +196,7 @@ impl TryFrom for TxQueryResponse { confirmations: proto_response.confirmations, is_synced: proto_response.is_synced, height_of_longest_chain: proto_response.height_of_longest_chain, + mined_timestamp: proto_response.mined_timestamp, }) } } @@ -206,6 +209,7 @@ impl From for proto::TxQueryResponse { confirmations: response.confirmations, is_synced: response.is_synced, height_of_longest_chain: response.height_of_longest_chain, + mined_timestamp: response.mined_timestamp, } } } @@ -227,6 +231,7 @@ impl TryFrom for TxQueryBatchResponse { block_hash: proto_response.block_hash, block_height: proto_response.block_height, confirmations: proto_response.confirmations, + mined_timestamp: proto_response.mined_timestamp, }) } } diff --git a/base_layer/core/src/base_node/rpc/service.rs b/base_layer/core/src/base_node/rpc/service.rs index 3d8e729c1d..1cb229a627 100644 --- a/base_layer/core/src/base_node/rpc/service.rs +++ b/base_layer/core/src/base_node/rpc/service.rs @@ -121,6 +121,7 @@ impl BaseNodeWalletRpcService { confirmations, is_synced: true, height_of_longest_chain: chain_metadata.height_of_longest_chain(), + mined_timestamp: Some(header.timestamp.as_u64()), }; return Ok(response); }, @@ -141,6 +142,7 @@ impl BaseNodeWalletRpcService { confirmations: 0, is_synced: true, height_of_longest_chain: chain_metadata.height_of_longest_chain(), + mined_timestamp: None, }, TxStorageResponse::ReorgPool | TxStorageResponse::NotStoredOrphan | @@ -153,6 +155,7 @@ impl BaseNodeWalletRpcService { confirmations: 0, is_synced: true, height_of_longest_chain: chain_metadata.height_of_longest_chain(), + mined_timestamp: None, }, }; Ok(mempool_response) @@ -292,6 +295,7 @@ impl BaseNodeWalletService for BaseNodeWalletRpc block_hash: response.block_hash, confirmations: response.confirmations, block_height: response.height_of_longest_chain - response.confirmations, + mined_timestamp: response.mined_timestamp, }); } Ok(Response::new(TxQueryBatchResponses { @@ -299,6 +303,7 @@ impl BaseNodeWalletService for BaseNodeWalletRpc is_synced, tip_hash: Some(metadata.best_block().clone()), height_of_longest_chain: metadata.height_of_longest_chain(), + tip_mined_timestamp: Some(metadata.timestamp()), })) } @@ -392,6 +397,7 @@ impl BaseNodeWalletService for BaseNodeWalletRpc PrunedOutput::Pruned { .. } => None, PrunedOutput::NotPruned { output } => Some(output.into()), }, + mined_timestamp: utxo.mined_timestamp, }) .collect(), })) diff --git a/base_layer/core/src/base_node/rpc/sync_utxos_by_block_task.rs b/base_layer/core/src/base_node/rpc/sync_utxos_by_block_task.rs index b9efcb44a9..76124b2fd5 100644 --- a/base_layer/core/src/base_node/rpc/sync_utxos_by_block_task.rs +++ b/base_layer/core/src/base_node/rpc/sync_utxos_by_block_task.rs @@ -154,6 +154,7 @@ where B: BlockchainBackend + 'static outputs: utxos, height: current_header.height, header_hash: current_header_hash, + mined_timestamp: current_header.timestamp.as_u64(), }; // Ensure task stops if the peer prematurely stops their RPC session if tx.send(Ok(utxo_block_response)).await.is_err() { diff --git a/base_layer/core/src/base_node/state_machine_service/states/listening.rs b/base_layer/core/src/base_node/state_machine_service/states/listening.rs index 828d96bed6..d01235c082 100644 --- a/base_layer/core/src/base_node/state_machine_service/states/listening.rs +++ b/base_layer/core/src/base_node/state_machine_service/states/listening.rs @@ -416,7 +416,7 @@ mod test { let best_network_metadata = best_claimed_metadata(&peer_metadata_list); assert!(best_network_metadata.is_none()); let best_network_metadata = ChainMetadata::empty(); - assert_eq!(best_network_metadata, ChainMetadata::new(0, Vec::new(), 0, 0, 0)); + assert_eq!(best_network_metadata, ChainMetadata::new(0, Vec::new(), 0, 0, 0, 0)); let sync_peers = select_sync_peers(&best_network_metadata, &peer_metadata_list); assert_eq!(sync_peers.len(), 0); @@ -428,7 +428,14 @@ mod test { // Archival node let peer1 = PeerChainMetadata::new( node_id1.clone(), - ChainMetadata::new(network_tip_height, block_hash1.clone(), 0, 0, accumulated_difficulty1), + ChainMetadata::new( + network_tip_height, + block_hash1.clone(), + 0, + 0, + accumulated_difficulty1, + 0, + ), None, ); @@ -441,6 +448,7 @@ mod test { 500, 5000 - 500, accumulated_difficulty1, + 0, ), None, ); @@ -453,6 +461,7 @@ mod test { 1440, 5000 - 1440, accumulated_difficulty1, + 0, ), None, ); @@ -464,6 +473,7 @@ mod test { 2880, 5000 - 2880, accumulated_difficulty2, + 0, ), None, ); @@ -476,6 +486,7 @@ mod test { 2880, 5000 - 2880, accumulated_difficulty1, + 0, ), None, ); @@ -498,40 +509,40 @@ mod test { #[test] fn sync_mode_selection() { - let local = ChainMetadata::new(0, Vec::new(), 0, 0, 500_000); + let local = ChainMetadata::new(0, Vec::new(), 0, 0, 500_000, 0); match determine_sync_mode(0, &local, &local, vec![]) { SyncStatus::UpToDate => {}, _ => panic!(), } - let network = ChainMetadata::new(0, Vec::new(), 0, 0, 499_000); + let network = ChainMetadata::new(0, Vec::new(), 0, 0, 499_000, 0); match determine_sync_mode(0, &local, &network, vec![]) { SyncStatus::UpToDate => {}, _ => panic!(), } - let network = ChainMetadata::new(0, Vec::new(), 0, 0, 500_001); + let network = ChainMetadata::new(0, Vec::new(), 0, 0, 500_001, 0); match determine_sync_mode(0, &local, &network, vec![]) { SyncStatus::Lagging { network: n, .. } => assert_eq!(n, network), _ => panic!(), } - let local = ChainMetadata::new(100, Vec::new(), 50, 50, 500_000); - let network = ChainMetadata::new(150, Vec::new(), 0, 0, 500_001); + let local = ChainMetadata::new(100, Vec::new(), 50, 50, 500_000, 0); + let network = ChainMetadata::new(150, Vec::new(), 0, 0, 500_001, 0); match determine_sync_mode(0, &local, &network, vec![]) { SyncStatus::Lagging { network: n, .. } => assert_eq!(n, network), _ => panic!(), } - let local = ChainMetadata::new(0, Vec::new(), 50, 50, 500_000); - let network = ChainMetadata::new(100, Vec::new(), 0, 0, 500_001); + let local = ChainMetadata::new(0, Vec::new(), 50, 50, 500_000, 0); + let network = ChainMetadata::new(100, Vec::new(), 0, 0, 500_001, 0); match determine_sync_mode(0, &local, &network, vec![]) { SyncStatus::Lagging { network: n, .. } => assert_eq!(n, network), _ => panic!(), } - let local = ChainMetadata::new(99, Vec::new(), 50, 50, 500_000); - let network = ChainMetadata::new(150, Vec::new(), 0, 0, 500_001); + let local = ChainMetadata::new(99, Vec::new(), 50, 50, 500_000, 0); + let network = ChainMetadata::new(150, Vec::new(), 0, 0, 500_001, 0); match determine_sync_mode(0, &local, &network, vec![]) { SyncStatus::Lagging { network: n, .. } => assert_eq!(n, network), _ => panic!(), diff --git a/base_layer/core/src/base_node/sync/block_sync/synchronizer.rs b/base_layer/core/src/base_node/sync/block_sync/synchronizer.rs index 64ee168e8f..663efd8fa2 100644 --- a/base_layer/core/src/base_node/sync/block_sync/synchronizer.rs +++ b/base_layer/core/src/base_node/sync/block_sync/synchronizer.rs @@ -246,6 +246,7 @@ impl BlockSynchronizer { let current_height = header.height(); let header_hash = header.hash().clone(); + let timestamp = header.timestamp(); if header.header().prev_hash != prev_hash { return Err(BlockSyncError::PeerSentBlockThatDidNotFormAChain { @@ -317,6 +318,7 @@ impl BlockSynchronizer { header_hash, block.accumulated_data().total_accumulated_difficulty, block.header().prev_hash.clone(), + timestamp, ) .commit() .await?; diff --git a/base_layer/core/src/base_node/sync/horizon_state_sync/synchronizer.rs b/base_layer/core/src/base_node/sync/horizon_state_sync/synchronizer.rs index 9aa8931471..a59a5f5422 100644 --- a/base_layer/core/src/base_node/sync/horizon_state_sync/synchronizer.rs +++ b/base_layer/core/src/base_node/sync/horizon_state_sync/synchronizer.rs @@ -531,6 +531,7 @@ impl<'a, B: BlockchainBackend + 'static> HorizonStateSynchronization<'a, B> { current_header.hash().clone(), current_header.height(), u32::try_from(mmr_position)?, + current_header.timestamp(), ); mmr_position += 1; }, @@ -553,6 +554,7 @@ impl<'a, B: BlockchainBackend + 'static> HorizonStateSynchronization<'a, B> { current_header.hash().clone(), current_header.height(), u32::try_from(mmr_position)?, + current_header.timestamp(), ); mmr_position += 1; }, @@ -764,6 +766,7 @@ impl<'a, B: BlockchainBackend + 'static> HorizonStateSynchronization<'a, B> { header.hash().clone(), header.accumulated_data().total_accumulated_difficulty, metadata.best_block().clone(), + header.timestamp(), ) .set_pruned_height(header.height()) .set_horizon_data(calc_kernel_sum, calc_utxo_sum) diff --git a/base_layer/core/src/blocks/accumulated_data.rs b/base_layer/core/src/blocks/accumulated_data.rs index 9b0c8cc1d8..645faf18cc 100644 --- a/base_layer/core/src/blocks/accumulated_data.rs +++ b/base_layer/core/src/blocks/accumulated_data.rs @@ -406,6 +406,10 @@ impl ChainHeader { self.header.height } + pub fn timestamp(&self) -> u64 { + self.header.timestamp.as_u64() + } + pub fn hash(&self) -> &HashOutput { &self.accumulated_data.hash } diff --git a/base_layer/core/src/chain_storage/async_db.rs b/base_layer/core/src/chain_storage/async_db.rs index 15206f281d..c57eff31d4 100644 --- a/base_layer/core/src/chain_storage/async_db.rs +++ b/base_layer/core/src/chain_storage/async_db.rs @@ -312,9 +312,15 @@ impl<'a, B: BlockchainBackend + 'static> AsyncDbTransaction<'a, B> { hash: HashOutput, accumulated_difficulty: u128, expected_prev_best_block: HashOutput, + timestamp: u64, ) -> &mut Self { - self.transaction - .set_best_block(height, hash, accumulated_difficulty, expected_prev_best_block); + self.transaction.set_best_block( + height, + hash, + accumulated_difficulty, + expected_prev_best_block, + timestamp, + ); self } @@ -344,9 +350,10 @@ impl<'a, B: BlockchainBackend + 'static> AsyncDbTransaction<'a, B> { header_hash: HashOutput, header_height: u64, mmr_position: u32, + timestamp: u64, ) -> &mut Self { self.transaction - .insert_utxo(output, header_hash, header_height, mmr_position); + .insert_utxo(output, header_hash, header_height, mmr_position, timestamp); self } @@ -357,9 +364,16 @@ impl<'a, B: BlockchainBackend + 'static> AsyncDbTransaction<'a, B> { header_hash: HashOutput, header_height: u64, mmr_position: u32, + timestamp: u64, ) -> &mut Self { - self.transaction - .insert_pruned_utxo(output_hash, witness_hash, header_hash, header_height, mmr_position); + self.transaction.insert_pruned_utxo( + output_hash, + witness_hash, + header_hash, + header_height, + mmr_position, + timestamp, + ); self } diff --git a/base_layer/core/src/chain_storage/blockchain_database.rs b/base_layer/core/src/chain_storage/blockchain_database.rs index cc8cba0643..05195eb6de 100644 --- a/base_layer/core/src/chain_storage/blockchain_database.rs +++ b/base_layer/core/src/chain_storage/blockchain_database.rs @@ -1450,11 +1450,18 @@ fn insert_best_block(txn: &mut DbTransaction, block: Arc) -> Result< } let height = block.height(); + let timestamp = block.header().timestamp().as_u64(); let accumulated_difficulty = block.accumulated_data().total_accumulated_difficulty; let expected_prev_best_block = block.block().header.prev_hash.clone(); txn.insert_chain_header(block.to_chain_header()) .insert_block_body(block) - .set_best_block(height, block_hash, accumulated_difficulty, expected_prev_best_block); + .set_best_block( + height, + block_hash, + accumulated_difficulty, + expected_prev_best_block, + timestamp, + ); Ok(()) } @@ -1765,6 +1772,7 @@ fn rewind_to_height( chain_header.accumulated_data().hash.clone(), chain_header.accumulated_data().total_accumulated_difficulty, expected_block_hash, + chain_header.timestamp(), ); db.write(txn)?; diff --git a/base_layer/core/src/chain_storage/db_transaction.rs b/base_layer/core/src/chain_storage/db_transaction.rs index 24b5249ab0..31602a59a7 100644 --- a/base_layer/core/src/chain_storage/db_transaction.rs +++ b/base_layer/core/src/chain_storage/db_transaction.rs @@ -116,12 +116,14 @@ impl DbTransaction { header_hash: HashOutput, header_height: u64, mmr_leaf_index: u32, + timestamp: u64, ) -> &mut Self { self.operations.push(WriteOperation::InsertOutput { header_hash, header_height, output: Box::new(utxo), mmr_position: mmr_leaf_index, + timestamp, }); self } @@ -133,6 +135,7 @@ impl DbTransaction { header_hash: HashOutput, header_height: u64, mmr_leaf_index: u32, + timestamp: u64, ) -> &mut Self { self.operations.push(WriteOperation::InsertPrunedOutput { header_hash, @@ -140,6 +143,7 @@ impl DbTransaction { output_hash, witness_hash, mmr_position: mmr_leaf_index, + timestamp, }); self } @@ -232,12 +236,14 @@ impl DbTransaction { hash: HashOutput, accumulated_difficulty: u128, expected_prev_best_block: HashOutput, + timestamp: u64, ) -> &mut Self { self.operations.push(WriteOperation::SetBestBlock { height, hash, accumulated_difficulty, expected_prev_best_block, + timestamp, }); self } @@ -303,6 +309,7 @@ pub enum WriteOperation { header_height: u64, output: Box, mmr_position: u32, + timestamp: u64, }, InsertPrunedOutput { header_hash: HashOutput, @@ -310,6 +317,7 @@ pub enum WriteOperation { output_hash: HashOutput, witness_hash: HashOutput, mmr_position: u32, + timestamp: u64, }, InsertBadBlock { hash: HashOutput, @@ -340,6 +348,7 @@ pub enum WriteOperation { hash: HashOutput, accumulated_difficulty: u128, expected_prev_best_block: HashOutput, + timestamp: u64, }, SetPruningHorizonConfig(u64), SetPrunedHeight { @@ -390,13 +399,15 @@ impl fmt::Display for WriteOperation { header_height, output, mmr_position, + timestamp, } => write!( f, - "Insert output {} in block:{},#{} position: {}", + "Insert output {} in block:{},#{} position: {}, timestamp: {}", output.hash().to_hex(), header_hash.to_hex(), header_height, - mmr_position + mmr_position, + timestamp ), DeleteOrphanChainTip(hash) => write!(f, "DeleteOrphanChainTip({})", hash.to_hex()), InsertOrphanChainTip(hash) => write!(f, "InsertOrphanChainTip({})", hash.to_hex()), @@ -411,6 +422,7 @@ impl fmt::Display for WriteOperation { output_hash: _, witness_hash: _, mmr_position: _, + timestamp: _, } => write!(f, "Insert pruned output"), UpdateBlockAccumulatedData { header_hash, .. } => { write!(f, "Update Block data for block {}", header_hash.to_hex()) @@ -428,12 +440,14 @@ impl fmt::Display for WriteOperation { hash, accumulated_difficulty, expected_prev_best_block: _, + timestamp, } => write!( f, - "Update best block to height:{} ({}) with difficulty: {}", + "Update best block to height:{} ({}) with difficulty: {} and timestamp : {}", height, hash.to_hex(), - accumulated_difficulty + accumulated_difficulty, + timestamp ), SetPruningHorizonConfig(pruning_horizon) => write!(f, "Set config: pruning horizon to {}", pruning_horizon), SetPrunedHeight { height, .. } => write!(f, "Set pruned height to {}", height), diff --git a/base_layer/core/src/chain_storage/lmdb_db/lmdb_db.rs b/base_layer/core/src/chain_storage/lmdb_db/lmdb_db.rs index 3e104630ba..d99ffb4f8f 100644 --- a/base_layer/core/src/chain_storage/lmdb_db/lmdb_db.rs +++ b/base_layer/core/src/chain_storage/lmdb_db/lmdb_db.rs @@ -325,8 +325,16 @@ impl LMDBDatabase { header_height, output, mmr_position, + timestamp, } => { - self.insert_output(&write_txn, header_hash, *header_height, &*output, *mmr_position)?; + self.insert_output( + &write_txn, + header_hash, + *header_height, + &*output, + *mmr_position, + *timestamp, + )?; }, InsertPrunedOutput { header_hash, @@ -334,6 +342,7 @@ impl LMDBDatabase { output_hash, witness_hash, mmr_position, + timestamp, } => { self.insert_pruned_output( &write_txn, @@ -342,6 +351,7 @@ impl LMDBDatabase { output_hash, witness_hash, *mmr_position, + *timestamp, )?; }, DeleteHeader(height) => { @@ -394,6 +404,7 @@ impl LMDBDatabase { hash, accumulated_difficulty, expected_prev_best_block, + timestamp, } => { // for security we check that the best block does exist, and we check the previous value // we dont want to check this if the prev block has never been set, this means a empty hash of 32 @@ -431,6 +442,11 @@ impl LMDBDatabase { MetadataKey::AccumulatedWork, &MetadataValue::AccumulatedWork(*accumulated_difficulty), )?; + self.set_metadata( + &write_txn, + MetadataKey::BestBlockTimestamp, + &MetadataValue::BestBlockTimestamp(*timestamp), + )?; }, SetPruningHorizonConfig(pruning_horizon) => { self.set_metadata( @@ -537,6 +553,7 @@ impl LMDBDatabase { header_height: u64, output: &TransactionOutput, mmr_position: u32, + timestamp: u64, ) -> Result<(), ChainStorageError> { let output_hash = output.hash(); let witness_hash = output.witness_hash(); @@ -595,6 +612,7 @@ impl LMDBDatabase { hash: output_hash, witness_hash, mined_height: header_height, + mined_timestamp: timestamp, }, "utxos_db", )?; @@ -610,6 +628,7 @@ impl LMDBDatabase { output_hash: &HashOutput, witness_hash: &HashOutput, mmr_position: u32, + timestamp: u64, ) -> Result<(), ChainStorageError> { if !lmdb_exists(txn, &self.block_hashes_db, header_hash.as_slice())? { return Err(ChainStorageError::InvalidOperation(format!( @@ -636,6 +655,7 @@ impl LMDBDatabase { hash: output_hash.clone(), witness_hash: witness_hash.clone(), mined_height: header_height, + mined_timestamp: timestamp, }, "utxos_db", ) @@ -1328,7 +1348,14 @@ impl LMDBDatabase { mmr_count )) })?; - self.insert_output(txn, &block_hash, header.height, &output, mmr_count)?; + self.insert_output( + txn, + &block_hash, + header.height, + &output, + mmr_count, + header.timestamp().as_u64(), + )?; } for commitment in spent_zero_conf_commitments { @@ -1567,12 +1594,14 @@ impl LMDBDatabase { mmr_position, mined_height, header_hash, + mined_timestamp, .. }) => Ok(Some(UtxoMinedInfo { output: PrunedOutput::NotPruned { output: o }, mmr_position, mined_height, header_hash, + mined_timestamp, })), Some(TransactionOutputRowData { output: None, @@ -1581,6 +1610,7 @@ impl LMDBDatabase { hash, witness_hash, header_hash, + mined_timestamp, .. }) => Ok(Some(UtxoMinedInfo { output: PrunedOutput::Pruned { @@ -1590,6 +1620,7 @@ impl LMDBDatabase { mmr_position, mined_height, header_hash, + mined_timestamp, })), _ => Ok(None), } @@ -2471,6 +2502,7 @@ fn fetch_metadata(txn: &ConstTransaction<'_>, db: &Database) -> Result, db: &Database) -> Result, db: &Database) -> Result { + let k = MetadataKey::BestBlockTimestamp; + let val: Option = lmdb_get(txn, db, &k.as_u32())?; + match val { + Some(MetadataValue::BestBlockTimestamp(timestamp)) => Ok(timestamp), + _ => Err(ChainStorageError::ValueNotFound { + entity: "ChainMetadata", + field: "BestBlockTimestamp", + value: "".to_string(), + }), + } +} + // Fetches the accumulated work from the provided metadata db. fn fetch_accumulated_work(txn: &ConstTransaction<'_>, db: &Database) -> Result { let k = MetadataKey::AccumulatedWork; @@ -2584,6 +2630,7 @@ enum MetadataKey { PrunedHeight, HorizonData, DeletedBitmap, + BestBlockTimestamp, } impl MetadataKey { @@ -2603,6 +2650,7 @@ impl fmt::Display for MetadataKey { MetadataKey::BestBlock => f.write_str("Chain tip block hash"), MetadataKey::HorizonData => f.write_str("Database info"), MetadataKey::DeletedBitmap => f.write_str("Deleted bitmap"), + MetadataKey::BestBlockTimestamp => f.write_str("Chain tip block timestamp"), } } } @@ -2617,6 +2665,7 @@ enum MetadataValue { PrunedHeight(u64), HorizonData(HorizonData), DeletedBitmap(DeletedBitmap), + BestBlockTimestamp(u64), } impl fmt::Display for MetadataValue { @@ -2631,6 +2680,7 @@ impl fmt::Display for MetadataValue { MetadataValue::DeletedBitmap(deleted) => { write!(f, "Deleted Bitmap ({} indexes)", deleted.bitmap().cardinality()) }, + MetadataValue::BestBlockTimestamp(timestamp) => write!(f, "Chain tip block timestamp is {}", timestamp), } } } diff --git a/base_layer/core/src/chain_storage/lmdb_db/mod.rs b/base_layer/core/src/chain_storage/lmdb_db/mod.rs index b349466398..5cd39d864a 100644 --- a/base_layer/core/src/chain_storage/lmdb_db/mod.rs +++ b/base_layer/core/src/chain_storage/lmdb_db/mod.rs @@ -42,6 +42,7 @@ pub(crate) struct TransactionOutputRowData { pub hash: HashOutput, pub witness_hash: HashOutput, pub mined_height: u64, + pub mined_timestamp: u64, } /// Transaction input row data taking references and used for serialization. diff --git a/base_layer/core/src/chain_storage/utxo_mined_info.rs b/base_layer/core/src/chain_storage/utxo_mined_info.rs index d8ec27c765..4eea1afa5b 100644 --- a/base_layer/core/src/chain_storage/utxo_mined_info.rs +++ b/base_layer/core/src/chain_storage/utxo_mined_info.rs @@ -31,4 +31,5 @@ pub struct UtxoMinedInfo { pub mmr_position: u32, pub mined_height: u64, pub header_hash: BlockHash, + pub mined_timestamp: u64, } diff --git a/base_layer/core/src/validation/test.rs b/base_layer/core/src/validation/test.rs index 1d97b3b798..552fd4fac5 100644 --- a/base_layer/core/src/validation/test.rs +++ b/base_layer/core/src/validation/test.rs @@ -223,7 +223,7 @@ fn chain_balance_validation() { let mut mmr_leaf_index = 4; txn.insert_kernel(kernel.clone(), header1.hash().clone(), mmr_position); - txn.insert_utxo(coinbase.clone(), header1.hash().clone(), 1, mmr_leaf_index); + txn.insert_utxo(coinbase.clone(), header1.hash().clone(), 1, mmr_leaf_index, 0); db.commit(txn).unwrap(); utxo_sum = &coinbase.commitment + &utxo_sum; @@ -273,7 +273,7 @@ fn chain_balance_validation() { utxo_sum = &coinbase.commitment + &utxo_sum; kernel_sum = &kernel.excess + &kernel_sum; mmr_leaf_index += 1; - txn.insert_utxo(coinbase, header2.hash().clone(), 2, mmr_leaf_index); + txn.insert_utxo(coinbase, header2.hash().clone(), 2, mmr_leaf_index, 0); mmr_position += 1; txn.insert_kernel(kernel, header2.hash().clone(), mmr_position); diff --git a/base_layer/core/tests/helpers/chain_metadata.rs b/base_layer/core/tests/helpers/chain_metadata.rs index 7e91a84e56..19d221cd65 100644 --- a/base_layer/core/tests/helpers/chain_metadata.rs +++ b/base_layer/core/tests/helpers/chain_metadata.rs @@ -73,6 +73,6 @@ pub fn random_peer_metadata(height: u64, difficulty: u128) -> PeerChainMetadata let key: Vec = (0..13).map(|_| rand::random::()).collect(); let id = NodeId::from_key(&key); let block_hash = Blake256::digest(id.as_bytes()).to_vec(); - let metadata = ChainMetadata::new(height, block_hash, 2800, 0, difficulty); + let metadata = ChainMetadata::new(height, block_hash, 2800, 0, difficulty, 0); PeerChainMetadata::new(id, metadata, None) } diff --git a/base_layer/core/tests/node_comms_interface.rs b/base_layer/core/tests/node_comms_interface.rs index 9e7c9b7b19..4eb4a5b780 100644 --- a/base_layer/core/tests/node_comms_interface.rs +++ b/base_layer/core/tests/node_comms_interface.rs @@ -259,14 +259,15 @@ async fn inbound_fetch_txos() { let block = store.fetch_block(0).unwrap().block().clone(); let header_hash = block.header.hash(); let mut txn = DbTransaction::new(); - txn.insert_utxo(utxo.clone(), header_hash.clone(), block.header.height, 6000); - txn.insert_utxo(stxo.clone(), header_hash.clone(), block.header.height, 6001); + txn.insert_utxo(utxo.clone(), header_hash.clone(), block.header.height, 6000, 0); + txn.insert_utxo(stxo.clone(), header_hash.clone(), block.header.height, 6001, 0); txn.insert_pruned_utxo( pruned_utxo_hash.clone(), pruned_utxo.witness_hash(), header_hash.clone(), 5, 6002, + 0, ); assert!(store.commit(txn).is_ok()); @@ -392,7 +393,7 @@ async fn inbound_fetch_blocks_before_horizon_height() { utxo.minimum_value_promise, ); let mut txn = DbTransaction::new(); - txn.insert_utxo(utxo.clone(), block0.hash().clone(), 0, 4002); + txn.insert_utxo(utxo.clone(), block0.hash().clone(), 0, 4002, 0); assert!(store.commit(txn).is_ok()); let txn = txn_schema!( diff --git a/base_layer/wallet/Cargo.toml b/base_layer/wallet/Cargo.toml index 9ac4fdf615..83c655ae2a 100644 --- a/base_layer/wallet/Cargo.toml +++ b/base_layer/wallet/Cargo.toml @@ -55,6 +55,7 @@ tempfile = "3.1.0" thiserror = "1.0.26" tower = "0.4" prost = "0.9" +itertools = "0.10.3" [dependencies.tari_core] path = "../../base_layer/core" diff --git a/base_layer/wallet/migrations/2022-07-04-150000_add_mined_timestamp/up.sql b/base_layer/wallet/migrations/2022-07-04-150000_add_mined_timestamp/up.sql new file mode 100644 index 0000000000..5c2870183d --- /dev/null +++ b/base_layer/wallet/migrations/2022-07-04-150000_add_mined_timestamp/up.sql @@ -0,0 +1,5 @@ +ALTER TABLE completed_transactions + ADD mined_timestamp DATETIME NULL; + +ALTER TABLE outputs + ADD mined_timestamp DATETIME NULL; \ No newline at end of file diff --git a/base_layer/wallet/src/output_manager_service/error.rs b/base_layer/wallet/src/output_manager_service/error.rs index d1b0862a09..8adcc975af 100644 --- a/base_layer/wallet/src/output_manager_service/error.rs +++ b/base_layer/wallet/src/output_manager_service/error.rs @@ -129,6 +129,10 @@ pub enum OutputManagerError { KeyManagerServiceError(#[from] KeyManagerServiceError), #[error("Value can't be encrypted/decrypted")] ValueEncryptionError(#[from] EncryptionError), + #[error("No commitments were provided")] + NoCommitmentsProvided, + #[error("Invalid argument: {0}")] + InvalidArgument(String), } #[derive(Debug, Error)] diff --git a/base_layer/wallet/src/output_manager_service/handle.rs b/base_layer/wallet/src/output_manager_service/handle.rs index c811333772..4a1260ed15 100644 --- a/base_layer/wallet/src/output_manager_service/handle.rs +++ b/base_layer/wallet/src/output_manager_service/handle.rs @@ -25,7 +25,7 @@ use std::{fmt, fmt::Formatter, sync::Arc}; use aes_gcm::Aes256Gcm; use tari_common_types::{ transaction::TxId, - types::{HashOutput, PublicKey}, + types::{Commitment, HashOutput, PublicKey}, }; use tari_core::{ covenants::Covenant, @@ -108,7 +108,14 @@ pub enum OutputManagerRequest { GetInvalidOutputs, ValidateUtxos, RevalidateTxos, - CreateCoinSplit((MicroTari, usize, MicroTari, Option)), + CreateCoinSplit((Vec, MicroTari, usize, MicroTari)), + CreateCoinSplitEven((Vec, usize, MicroTari)), + PreviewCoinJoin((Vec, MicroTari)), + PreviewCoinSplitEven((Vec, usize, MicroTari)), + CreateCoinJoin { + commitments: Vec, + fee_per_gram: MicroTari, + }, ApplyEncryption(Box), RemoveEncryption, FeeEstimate { @@ -165,7 +172,26 @@ impl fmt::Display for OutputManagerRequest { GetInvalidOutputs => write!(f, "GetInvalidOutputs"), ValidateUtxos => write!(f, "ValidateUtxos"), RevalidateTxos => write!(f, "RevalidateTxos"), - CreateCoinSplit(v) => write!(f, "CreateCoinSplit ({})", v.0), + PreviewCoinJoin((commitments, fee_per_gram)) => write!( + f, + "PreviewCoinJoin(commitments={:#?}, fee_per_gram={})", + commitments, fee_per_gram + ), + PreviewCoinSplitEven((commitments, number_of_splits, fee_per_gram)) => write!( + f, + "PreviewCoinSplitEven(commitments={:#?}, number_of_splits={}, fee_per_gram={})", + commitments, number_of_splits, fee_per_gram + ), + CreateCoinSplit(v) => write!(f, "CreateCoinSplit ({:?})", v.0), + CreateCoinSplitEven(v) => write!(f, "CreateCoinSplitEven ({:?})", v.0), + CreateCoinJoin { + commitments, + fee_per_gram, + } => write!( + f, + "CreateCoinJoin: commitments={:#?}, fee_per_gram={}", + commitments, fee_per_gram, + ), ApplyEncryption(_) => write!(f, "ApplyEncryption"), RemoveEncryption => write!(f, "RemoveEncryption"), GetCoinbaseTransaction(_) => write!(f, "GetCoinbaseTransaction"), @@ -242,6 +268,7 @@ pub enum OutputManagerResponse { CoinbaseAbandonedSet, ClaimHtlcTransaction((TxId, MicroTari, MicroTari, Transaction)), OutputStatusesByTxId(OutputStatusesByTxId), + CoinPreview((Vec, MicroTari)), } pub type OutputManagerEventSender = broadcast::Sender>; @@ -610,22 +637,57 @@ impl OutputManagerHandle { } } + pub async fn preview_coin_join_with_commitments( + &mut self, + commitments: Vec, + fee_per_gram: MicroTari, + ) -> Result<(Vec, MicroTari), OutputManagerError> { + match self + .handle + .call(OutputManagerRequest::PreviewCoinJoin((commitments, fee_per_gram))) + .await?? + { + OutputManagerResponse::CoinPreview((expected_outputs, fee)) => Ok((expected_outputs, fee)), + _ => Err(OutputManagerError::UnexpectedApiResponse), + } + } + + pub async fn preview_coin_split_with_commitments_no_amount( + &mut self, + commitments: Vec, + split_count: usize, + fee_per_gram: MicroTari, + ) -> Result<(Vec, MicroTari), OutputManagerError> { + match self + .handle + .call(OutputManagerRequest::PreviewCoinSplitEven(( + commitments, + split_count, + fee_per_gram, + ))) + .await?? + { + OutputManagerResponse::CoinPreview((expected_outputs, fee)) => Ok((expected_outputs, fee)), + _ => Err(OutputManagerError::UnexpectedApiResponse), + } + } + /// Create a coin split transaction. /// Returns (tx_id, tx, utxos_total_value). pub async fn create_coin_split( &mut self, + commitments: Vec, amount_per_split: MicroTari, split_count: usize, fee_per_gram: MicroTari, - lock_height: Option, ) -> Result<(TxId, Transaction, MicroTari), OutputManagerError> { match self .handle .call(OutputManagerRequest::CreateCoinSplit(( + commitments, amount_per_split, split_count, fee_per_gram, - lock_height, ))) .await?? { @@ -634,6 +696,44 @@ impl OutputManagerHandle { } } + pub async fn create_coin_split_even( + &mut self, + commitments: Vec, + split_count: usize, + fee_per_gram: MicroTari, + ) -> Result<(TxId, Transaction, MicroTari), OutputManagerError> { + match self + .handle + .call(OutputManagerRequest::CreateCoinSplitEven(( + commitments, + split_count, + fee_per_gram, + ))) + .await?? + { + OutputManagerResponse::Transaction(ct) => Ok(ct), + _ => Err(OutputManagerError::UnexpectedApiResponse), + } + } + + pub async fn create_coin_join( + &mut self, + commitments: Vec, + fee_per_gram: MicroTari, + ) -> Result<(TxId, Transaction, MicroTari), OutputManagerError> { + match self + .handle + .call(OutputManagerRequest::CreateCoinJoin { + commitments, + fee_per_gram, + }) + .await?? + { + OutputManagerResponse::Transaction(result) => Ok(result), + _ => Err(OutputManagerError::UnexpectedApiResponse), + } + } + pub async fn create_htlc_refund_transaction( &mut self, output: HashOutput, diff --git a/base_layer/wallet/src/output_manager_service/input_selection.rs b/base_layer/wallet/src/output_manager_service/input_selection.rs index aa24e5bd9d..4adcf0dc46 100644 --- a/base_layer/wallet/src/output_manager_service/input_selection.rs +++ b/base_layer/wallet/src/output_manager_service/input_selection.rs @@ -54,6 +54,13 @@ impl UtxoSelectionCriteria { } } + pub fn smallest_first() -> Self { + Self { + filter: UtxoSelectionFilter::Standard, + ordering: UtxoSelectionOrdering::SmallestFirst, + } + } + pub fn for_token(unique_id: Vec, parent_public_key: Option) -> Self { Self { filter: UtxoSelectionFilter::TokenOutput { @@ -73,6 +80,13 @@ impl UtxoSelectionCriteria { ..Default::default() } } + + pub fn specific(commitments: Vec) -> Self { + Self { + filter: UtxoSelectionFilter::SpecificOutputs { commitments }, + ordering: UtxoSelectionOrdering::Default, + } + } } impl Display for UtxoSelectionCriteria { @@ -128,7 +142,7 @@ pub enum UtxoSelectionFilter { output_type: OutputType, }, /// Selects specific outputs. All outputs must be exist and be spendable. - SpecificOutputs { outputs: Vec }, + SpecificOutputs { commitments: Vec }, } impl UtxoSelectionFilter { pub fn is_standard(&self) -> bool { @@ -155,7 +169,7 @@ impl Display for UtxoSelectionFilter { UtxoSelectionFilter::TokenOutput { .. } => { write!(f, "TokenOutput{{..}}") }, - UtxoSelectionFilter::SpecificOutputs { outputs } => { + UtxoSelectionFilter::SpecificOutputs { commitments: outputs } => { write!(f, "Specific({} output(s))", outputs.len()) }, UtxoSelectionFilter::ContractOutput { contract_id, .. } => { diff --git a/base_layer/wallet/src/output_manager_service/service.rs b/base_layer/wallet/src/output_manager_service/service.rs index 0b46166db1..e7e41f8528 100644 --- a/base_layer/wallet/src/output_manager_service/service.rs +++ b/base_layer/wallet/src/output_manager_service/service.rs @@ -25,12 +25,16 @@ use std::{convert::TryInto, fmt, sync::Arc}; use blake2::Digest; use diesel::result::{DatabaseErrorKind, Error as DieselError}; use futures::{pin_mut, StreamExt}; +use itertools::{ + FoldWhile::{Continue, Done}, + Itertools, +}; use log::*; use rand::{rngs::OsRng, RngCore}; use strum::IntoEnumIterator; use tari_common_types::{ transaction::TxId, - types::{BlockHash, HashOutput, PrivateKey, PublicKey}, + types::{BlockHash, Commitment, HashOutput, PrivateKey, PublicKey}, }; use tari_comms::{types::CommsPublicKey, NodeIdentity}; use tari_core::{ @@ -45,6 +49,7 @@ use tari_core::{ KernelFeatures, OutputFeatures, Transaction, + TransactionInput, TransactionError, TransactionOutput, TransactionOutputVersion, @@ -355,8 +360,50 @@ where let outputs = self.fetch_invalid_outputs()?.into_iter().map(|v| v.into()).collect(); Ok(OutputManagerResponse::InvalidOutputs(outputs)) }, - OutputManagerRequest::CreateCoinSplit((amount_per_split, split_count, fee_per_gram, lock_height)) => self - .create_coin_split(amount_per_split, split_count, fee_per_gram, lock_height) + OutputManagerRequest::PreviewCoinJoin((commitments, fee_per_gram)) => { + Ok(OutputManagerResponse::CoinPreview( + self.preview_coin_join_with_commitments(commitments, fee_per_gram) + .await?, + )) + }, + OutputManagerRequest::PreviewCoinSplitEven((commitments, number_of_splits, fee_per_gram)) => { + Ok(OutputManagerResponse::CoinPreview( + self.preview_coin_split_with_commitments_no_amount(commitments, number_of_splits, fee_per_gram) + .await?, + )) + }, + OutputManagerRequest::CreateCoinSplit((commitments, amount_per_split, split_count, fee_per_gram)) => { + if commitments.is_empty() { + self.create_coin_split_auto(Some(amount_per_split), split_count, fee_per_gram) + .await + .map(OutputManagerResponse::Transaction) + } else { + self.create_coin_split_with_commitments( + commitments, + Some(amount_per_split), + split_count, + fee_per_gram, + ) + .await + .map(OutputManagerResponse::Transaction) + } + }, + OutputManagerRequest::CreateCoinSplitEven((commitments, split_count, fee_per_gram)) => { + if commitments.is_empty() { + self.create_coin_split_auto(None, split_count, fee_per_gram) + .await + .map(OutputManagerResponse::Transaction) + } else { + self.create_coin_split_with_commitments(commitments, None, split_count, fee_per_gram) + .await + .map(OutputManagerResponse::Transaction) + } + }, + OutputManagerRequest::CreateCoinJoin { + commitments, + fee_per_gram, + } => self + .create_coin_join(commitments, fee_per_gram) .await .map(OutputManagerResponse::Transaction), OutputManagerRequest::ApplyEncryption(cipher) => self @@ -1330,6 +1377,137 @@ where Ok(()) } + // NOTE: WIP + #[allow(dead_code)] + async fn select_utxos2( + &mut self, + target_amount: MicroTari, + fee_per_gram: MicroTari, + num_outputs: usize, + total_output_metadata_byte_size: usize, + selection_criteria: UtxoSelectionCriteria, + ) -> Result { + debug!( + target: LOG_TARGET, + "select_utxos target_amount: {}, fee_per_gram: {}, num_outputs: {}, output_metadata_byte_size: {}, \ + selection_criteria: {:?}", + target_amount, + fee_per_gram, + num_outputs, + total_output_metadata_byte_size, + selection_criteria + ); + + let tip_height = self + .base_node_service + .get_chain_metadata() + .await? + .as_ref() + .map(|m| m.height_of_longest_chain()); + let balance = self.get_balance(tip_height)?; + + // collecting UTXOs sufficient to cover the target amount + let (_, accumulated_amount, utxos) = self + .resources + .db + .fetch_unspent_outputs_for_spending(selection_criteria.clone(), target_amount, tip_height)? + .into_iter() + .fold_while( + (1usize, MicroTari::zero(), Vec::::new()), + |mut acc, out| { + let fee = self.get_fee_calc().calculate( + fee_per_gram, + 1, + acc.0, + num_outputs, + total_output_metadata_byte_size, + ); + + let next = acc.1 + out.unblinded_output.value; + let target_amount_with_fee = target_amount + fee; + + // if next < target_amount_with_fee || acc.1 < target_amount_with_fee && next >= + // target_amount_with_fee + if next < target_amount_with_fee || acc.1 < target_amount_with_fee { + acc.0 += 1; + acc.1 = next; + acc.2.push(out); + Continue(acc) + } else { + Done(acc) + } + }, + ) + .into_inner(); + + // let accumulated_amount = utxos + // .iter() + // .fold(MicroTari::zero(), |acc, x| acc + x.unblinded_output.value); + + if accumulated_amount <= target_amount { + return Err(OutputManagerError::NotEnoughFunds); + } + + let fee_without_change = self.get_fee_calc().calculate( + fee_per_gram, + 1, + utxos.len(), + num_outputs, + total_output_metadata_byte_size, + ); + + // checking whether the total output value is enough + if accumulated_amount < (target_amount + fee_without_change) { + return Err(OutputManagerError::NotEnoughFunds); + } + + let fee_with_change = match accumulated_amount + .saturating_sub(target_amount + fee_without_change) + .as_u64() + { + 0 => fee_without_change, + _ => self.get_fee_calc().calculate( + fee_per_gram, + 1, + utxos.len(), + num_outputs + 1, + total_output_metadata_byte_size + self.default_metadata_size(), + ), + }; + + // this is how much it would require in the end + let target_amount_with_fee = target_amount + fee_with_change; + + // checking, again, whether a total output value is enough + if accumulated_amount < target_amount_with_fee { + return Err(OutputManagerError::NotEnoughFunds); + } + + // balance check + if balance.available_balance < target_amount_with_fee { + return if accumulated_amount + balance.pending_incoming_balance >= target_amount_with_fee { + Err(OutputManagerError::FundsPending) + } else { + Err(OutputManagerError::NotEnoughFunds) + }; + } + + trace!( + target: LOG_TARGET, + "select_utxos selection criteria: {}\noutputs found: {}", + selection_criteria, + utxos.len() + ); + + Ok(UtxoSelection { + utxos, + requires_change_output: accumulated_amount.saturating_sub(target_amount_with_fee) > MicroTari::zero(), + total_value: accumulated_amount, + fee_without_change, + fee_with_change, + }) + } + /// Select which unspent transaction outputs to use to send a transaction of the specified amount. Use the specified /// selection strategy to choose the outputs. It also determines if a change output is required. #[allow(clippy::too_many_lines)] @@ -1338,7 +1516,7 @@ where amount: MicroTari, fee_per_gram: MicroTari, num_outputs: usize, - output_metadata_byte_size: usize, + total_output_metadata_byte_size: usize, selection_criteria: UtxoSelectionCriteria, ) -> Result { debug!( @@ -1348,7 +1526,7 @@ where amount, fee_per_gram, num_outputs, - output_metadata_byte_size, + total_output_metadata_byte_size, selection_criteria ); let mut utxos = Vec::new(); @@ -1418,8 +1596,13 @@ where error!(target: LOG_TARGET, "-- utxos_total_value = {:?}", utxos_total_value); utxos.push(o); // The assumption here is that the only output will be the payment output and change if required - fee_without_change = - fee_calc.calculate(fee_per_gram, 1, utxos.len(), num_outputs, output_metadata_byte_size); + fee_without_change = fee_calc.calculate( + fee_per_gram, + 1, + utxos.len(), + num_outputs, + total_output_metadata_byte_size, + ); if utxos_total_value == amount + fee_without_change { break; } @@ -1428,7 +1611,7 @@ where 1, utxos.len(), num_outputs + 1, - output_metadata_byte_size + default_metadata_size, + total_output_metadata_byte_size + default_metadata_size, ); error!(target: LOG_TARGET, "-- amt+fee = {:?} {}", amount, fee_with_change); @@ -1482,73 +1665,432 @@ where Ok(()) } + fn default_metadata_size(&self) -> usize { + self.resources + .consensus_constants + .transaction_weight() + .round_up_metadata_size( + script!(Nop).consensus_encode_exact_size() + OutputFeatures::default().consensus_encode_exact_size(), + ) + } + + pub async fn preview_coin_join_with_commitments( + &self, + commitments: Vec, + fee_per_gram: MicroTari, + ) -> Result<(Vec, MicroTari), OutputManagerError> { + let src_outputs = self.resources.db.fetch_unspent_outputs_for_spending( + UtxoSelectionCriteria::specific(commitments), + MicroTari::zero(), + None, + )?; + + let accumulated_amount = src_outputs + .iter() + .fold(MicroTari::zero(), |acc, x| acc + x.unblinded_output.value); + + let fee = self + .get_fee_calc() + .calculate(fee_per_gram, 1, src_outputs.len(), 1, self.default_metadata_size()); + + Ok((vec![accumulated_amount.saturating_sub(fee)], fee)) + } + + pub async fn preview_coin_split_with_commitments_no_amount( + &mut self, + commitments: Vec, + number_of_splits: usize, + fee_per_gram: MicroTari, + ) -> Result<(Vec, MicroTari), OutputManagerError> { + if commitments.is_empty() { + return Err(OutputManagerError::NoCommitmentsProvided); + } + + if number_of_splits == 0 { + return Err(OutputManagerError::InvalidArgument( + "number_of_splits must be greater than 0".to_string(), + )); + } + + let src_outputs = self.resources.db.fetch_unspent_outputs_for_spending( + UtxoSelectionCriteria::specific(commitments), + MicroTari::zero(), + None, + )?; + + let fee = self.get_fee_calc().calculate( + fee_per_gram, + 1, + src_outputs.len(), + number_of_splits, + self.default_metadata_size() * number_of_splits, + ); + + let accumulated_amount = src_outputs + .iter() + .fold(MicroTari::zero(), |acc, x| acc + x.unblinded_output.value); + + let aftertax_amount = accumulated_amount.saturating_sub(fee); + let amount_per_split = MicroTari(aftertax_amount.as_u64() / number_of_splits as u64); + let unspent_remainder = MicroTari(aftertax_amount.as_u64() % amount_per_split.as_u64()); + let mut expected_outputs = vec![]; + + for i in 1..=number_of_splits { + expected_outputs.push(if i == number_of_splits { + amount_per_split + unspent_remainder + } else { + amount_per_split + }); + } + + Ok((expected_outputs, fee)) + } + + async fn create_coin_split_with_commitments( + &mut self, + commitments: Vec, + amount_per_split: Option, + number_of_splits: usize, + fee_per_gram: MicroTari, + ) -> Result<(TxId, Transaction, MicroTari), OutputManagerError> { + if commitments.is_empty() { + return Err(OutputManagerError::NoCommitmentsProvided); + } + + let src_outputs = self.resources.db.fetch_unspent_outputs_for_spending( + UtxoSelectionCriteria::specific(commitments), + MicroTari::zero(), + None, + )?; + + match amount_per_split { + None => { + self.create_coin_split_even(src_outputs, number_of_splits, fee_per_gram) + .await + }, + Some(amount_per_split) => { + self.create_coin_split(src_outputs, amount_per_split, number_of_splits, fee_per_gram) + .await + }, + } + } + + async fn create_coin_split_auto( + &mut self, + amount_per_split: Option, + number_of_splits: usize, + fee_per_gram: MicroTari, + ) -> Result<(TxId, Transaction, MicroTari), OutputManagerError> { + match amount_per_split { + None => Err(OutputManagerError::InvalidArgument( + "coin split without `amount_per_split` is not supported yet".to_string(), + )), + Some(amount_per_split) => { + let selection = self + .select_utxos( + amount_per_split * MicroTari(number_of_splits as u64), + fee_per_gram, + number_of_splits, + self.default_metadata_size() * number_of_splits, + UtxoSelectionCriteria::largest_first(), + ) + .await?; + + self.create_coin_split(selection.utxos, amount_per_split, number_of_splits, fee_per_gram) + .await + }, + } + } + #[allow(clippy::too_many_lines)] - async fn create_coin_split( + async fn create_coin_split_even( &mut self, - amount_per_split: MicroTari, - split_count: usize, + src_outputs: Vec, + number_of_splits: usize, fee_per_gram: MicroTari, - lock_height: Option, ) -> Result<(TxId, Transaction, MicroTari), OutputManagerError> { + if number_of_splits == 0 { + return Err(OutputManagerError::InvalidArgument( + "number_of_splits must be greater than 0".to_string(), + )); + } + + let covenant = Covenant::default(); + let default_metadata_size = self.default_metadata_size(); + let mut dest_outputs = Vec::with_capacity(number_of_splits + 1); + + // accumulated value amount from given source outputs + let accumulated_amount = src_outputs + .iter() + .fold(MicroTari::zero(), |acc, x| acc + x.unblinded_output.value); + + let fee = self.get_fee_calc().calculate( + fee_per_gram, + 1, + src_outputs.len(), + number_of_splits, + default_metadata_size * number_of_splits, + ); + + let aftertax_amount = accumulated_amount.saturating_sub(fee); + let amount_per_split = MicroTari(aftertax_amount.as_u64() / number_of_splits as u64); + let unspent_remainder = MicroTari(aftertax_amount.as_u64() % amount_per_split.as_u64()); + + // preliminary balance check + if self.get_balance(None)?.available_balance < (aftertax_amount + fee) { + return Err(OutputManagerError::NotEnoughFunds); + } + + trace!(target: LOG_TARGET, "initializing new split (even) transaction"); + + let mut tx_builder = SenderTransactionProtocol::builder(0, self.resources.consensus_constants.clone()); + tx_builder + .with_lock_height(0) + .with_fee_per_gram(fee_per_gram) + .with_offset(PrivateKey::random(&mut OsRng)) + .with_private_nonce(PrivateKey::random(&mut OsRng)) + .with_rewindable_outputs(self.resources.rewind_data.clone()); + + // collecting inputs from source outputs + let inputs: Vec = src_outputs + .iter() + .map(|src_out| { + src_out + .unblinded_output + .as_transaction_input(&self.resources.factories.commitment) + }) + .try_collect()?; + + // adding inputs to the transaction + src_outputs.iter().zip(inputs).for_each(|(src_output, input)| { + trace!( + target: LOG_TARGET, + "adding transaction input: output_hash=: {:?}", + src_output.hash + ); + tx_builder.with_input(input, src_output.unblinded_output.clone()); + }); + + for i in 1..=number_of_splits { + // NOTE: adding the unspent `change` to the last output + let amount_per_split = if i == number_of_splits { + amount_per_split + unspent_remainder + } else { + amount_per_split + }; + + let noop_script = script!(Nop); + let (spending_key, script_private_key) = self.get_spend_and_script_keys().await?; + let output_features = OutputFeatures { + recovery_byte: self.calculate_recovery_byte(spending_key.clone(), accumulated_amount.as_u64(), true)?, + ..Default::default() + }; + + // generating sender's keypair + let sender_offset_private_key = PrivateKey::random(&mut OsRng); + let sender_offset_public_key = PublicKey::from_secret_key(&sender_offset_private_key); + + let commitment_signature = TransactionOutput::create_final_metadata_signature( + TransactionOutputVersion::get_current_version(), + amount_per_split, + &spending_key, + &noop_script, + &output_features, + &sender_offset_private_key, + &covenant, + )?; + + let output = DbUnblindedOutput::rewindable_from_unblinded_output( + UnblindedOutput::new_current_version( + amount_per_split, + spending_key, + output_features, + noop_script, + inputs!(PublicKey::from_secret_key(&script_private_key)), + script_private_key, + sender_offset_public_key, + commitment_signature, + 0, + covenant.clone(), + ), + &self.resources.factories, + &self.resources.rewind_data.clone(), + None, + None, + )?; + + tx_builder + .with_output(output.unblinded_output.clone(), sender_offset_private_key) + .map_err(|e| OutputManagerError::BuildError(e.message))?; + + dest_outputs.push(output); + } + + let mut stp = tx_builder + .build::( + &self.resources.factories, + None, + self.last_seen_tip_height.unwrap_or(u64::MAX), + ) + .map_err(|e| OutputManagerError::BuildError(e.message))?; + + // The Transaction Protocol built successfully so we will pull the unspent outputs out of the unspent list and + // store them until the transaction times out OR is confirmed + let tx_id = stp.get_tx_id()?; + trace!( target: LOG_TARGET, - "Select UTXOs and estimate coin split transaction fee." + "Encumber coin split (even) transaction (tx_id={}) outputs", + tx_id ); - let output_count = split_count; - let script = script!(Nop); + + // encumbering transaction + self.resources + .db + .encumber_outputs(tx_id, src_outputs.clone(), dest_outputs)?; + self.confirm_encumberance(tx_id)?; + + trace!( + target: LOG_TARGET, + "finalizing coin split transaction (tx_id={}).", + tx_id + ); + + // finalizing transaction + stp.finalize( + KernelFeatures::empty(), + &self.resources.factories, + None, + self.last_seen_tip_height.unwrap_or(u64::MAX), + )?; + + Ok((tx_id, stp.take_transaction()?, aftertax_amount + fee)) + } + + #[allow(clippy::too_many_lines)] + async fn create_coin_split( + &mut self, + src_outputs: Vec, + amount_per_split: MicroTari, + number_of_splits: usize, + fee_per_gram: MicroTari, + ) -> Result<(TxId, Transaction, MicroTari), OutputManagerError> { + if number_of_splits == 0 { + return Err(OutputManagerError::InvalidArgument( + "number_of_splits must be greater than 0".to_string(), + )); + } + + if amount_per_split == MicroTari::zero() { + return Err(OutputManagerError::InvalidArgument( + "amount_per_split must be greater than 0".to_string(), + )); + } + let covenant = Covenant::default(); - let output_features_estimate = OutputFeatures::default(); - let metadata_byte_size = self - .resources - .consensus_constants - .transaction_weight() - .round_up_metadata_size( - output_features_estimate.consensus_encode_exact_size() + - script.consensus_encode_exact_size() + - covenant.consensus_encode_exact_size(), + let default_metadata_size = self.default_metadata_size(); + let mut dest_outputs = Vec::with_capacity(number_of_splits + 1); + let total_split_amount = MicroTari::from(amount_per_split.as_u64() * number_of_splits as u64); + + // accumulated value amount from given source outputs + let accumulated_amount = src_outputs + .iter() + .fold(MicroTari::zero(), |acc, x| acc + x.unblinded_output.value); + + if total_split_amount >= accumulated_amount { + return Err(OutputManagerError::NotEnoughFunds); + } + + let fee_without_change = self.get_fee_calc().calculate( + fee_per_gram, + 1, + src_outputs.len(), + number_of_splits, + default_metadata_size * number_of_splits, + ); + + // checking whether a total output value is enough + if accumulated_amount < (total_split_amount + fee_without_change) { + error!( + target: LOG_TARGET, + "failed to split coins, not enough funds with `fee_without_change` included" ); + return Err(OutputManagerError::NotEnoughFunds); + } - let total_split_amount = amount_per_split * split_count as u64; - let input_selection = self - .select_utxos( - total_split_amount, + let final_fee = match accumulated_amount + .saturating_sub(total_split_amount + fee_without_change) + .as_u64() + { + 0 => fee_without_change, + _ => self.get_fee_calc().calculate( fee_per_gram, - output_count, - output_count * metadata_byte_size, - UtxoSelectionCriteria::largest_first(), - ) - .await?; + 1, + src_outputs.len(), + number_of_splits + 1, + default_metadata_size * (number_of_splits + 1), + ), + }; - trace!(target: LOG_TARGET, "Construct coin split transaction."); - let offset = PrivateKey::random(&mut OsRng); - let nonce = PrivateKey::random(&mut OsRng); + // checking, again, whether a total output value is enough + if accumulated_amount < (total_split_amount + final_fee) { + error!( + target: LOG_TARGET, + "failed to split coins, not enough funds with `final_fee` included" + ); + return Err(OutputManagerError::NotEnoughFunds); + } - let mut builder = SenderTransactionProtocol::builder(0, self.resources.consensus_constants.clone()); - builder - .with_lock_height(lock_height.unwrap_or(0)) + // preliminary balance check + if self.get_balance(None)?.available_balance < (total_split_amount + final_fee) { + return Err(OutputManagerError::NotEnoughFunds); + } + + // NOTE: called `leftover` to remove possible brainlag by confusing `change` as a verb + let leftover_change = accumulated_amount.saturating_sub(total_split_amount + final_fee); + + // ---------------------------------------------------------------------------- + // initializing new transaction + + trace!(target: LOG_TARGET, "initializing new split transaction"); + + let mut tx_builder = SenderTransactionProtocol::builder(0, self.resources.consensus_constants.clone()); + tx_builder + .with_lock_height(0) .with_fee_per_gram(fee_per_gram) - .with_offset(offset.clone()) - .with_private_nonce(nonce.clone()) + .with_offset(PrivateKey::random(&mut OsRng)) + .with_private_nonce(PrivateKey::random(&mut OsRng)) .with_rewindable_outputs(self.resources.rewind_data.clone()); - trace!(target: LOG_TARGET, "Add inputs to coin split transaction."); - for uo in input_selection.iter() { - builder.with_input( - uo.unblinded_output - .as_transaction_input(&self.resources.factories.commitment)?, - uo.unblinded_output.clone(), + // collecting inputs from source outputs + let inputs: Vec = src_outputs + .iter() + .map(|src_out| { + src_out + .unblinded_output + .as_transaction_input(&self.resources.factories.commitment) + }) + .try_collect()?; + + // adding inputs to the transaction + src_outputs.iter().zip(inputs).for_each(|(src_output, input)| { + trace!( + target: LOG_TARGET, + "adding transaction input: output_hash=: {:?}", + src_output.hash ); - } + tx_builder.with_input(input, src_output.unblinded_output.clone()); + }); - let utxos_total_value = input_selection.total_value(); - trace!(target: LOG_TARGET, "Add outputs to coin split transaction."); - let mut outputs: Vec = Vec::with_capacity(output_count); - for _ in 0..output_count { - let output_amount = amount_per_split; + // ---------------------------------------------------------------------------- + // initializing primary outputs + for _ in 0..number_of_splits { + let noop_script = script!(Nop); let (spending_key, script_private_key) = self.get_spend_and_script_keys().await?; let output_features = OutputFeatures::default(); + // generating sender's keypair let sender_offset_private_key = PrivateKey::random(&mut OsRng); let sender_offset_public_key = PublicKey::from_secret_key(&sender_offset_private_key); let commitment = self @@ -1559,27 +2101,28 @@ where let encrypted_value = EncryptedValue::encrypt_value(&self.resources.rewind_data.encryption_key, &commitment, output_amount)?; let minimum_value_promise = MicroTari::zero(); - let metadata_signature = TransactionOutput::create_final_metadata_signature( + let commitment_signature = TransactionOutput::create_final_metadata_signature( TransactionOutputVersion::get_current_version(), - output_amount, + amount_per_split, &spending_key, - &script, + &noop_script, &output_features, &sender_offset_private_key, &covenant, &encrypted_value, minimum_value_promise, )?; - let utxo = DbUnblindedOutput::rewindable_from_unblinded_output( + + let output = DbUnblindedOutput::rewindable_from_unblinded_output( UnblindedOutput::new_current_version( - output_amount, + amount_per_split, spending_key, output_features, - script.clone(), + noop_script, inputs!(PublicKey::from_secret_key(&script_private_key)), script_private_key, sender_offset_public_key, - metadata_signature, + commitment_signature, 0, covenant.clone(), encrypted_value, @@ -1590,48 +2133,58 @@ where None, None, )?; - builder - .with_output(utxo.unblinded_output.clone(), sender_offset_private_key) + + tx_builder + .with_output(output.unblinded_output.clone(), sender_offset_private_key) .map_err(|e| OutputManagerError::BuildError(e.message))?; - outputs.push(utxo); + + dest_outputs.push(output); } - if input_selection.requires_change_output() { + let has_leftover_change = leftover_change > MicroTari::zero(); + + // extending transaction if there is some `change` left over + if has_leftover_change { let (spending_key, script_private_key) = self.get_spend_and_script_keys().await?; - builder.with_change_secret(spending_key); - builder.with_rewindable_outputs(self.resources.rewind_data.clone()); - builder.with_change_script( + tx_builder.with_change_secret(spending_key); + tx_builder.with_rewindable_outputs(self.resources.rewind_data.clone()); + tx_builder.with_change_script( script!(Nop), inputs!(PublicKey::from_secret_key(&script_private_key)), script_private_key, ); } - let factories = CryptoFactories::default(); - let mut stp = builder + let mut stp = tx_builder .build::( &self.resources.factories, None, self.last_seen_tip_height.unwrap_or(u64::MAX), ) .map_err(|e| OutputManagerError::BuildError(e.message))?; + // The Transaction Protocol built successfully so we will pull the unspent outputs out of the unspent list and // store them until the transaction times out OR is confirmed let tx_id = stp.get_tx_id()?; + trace!( target: LOG_TARGET, - "Encumber coin split transaction ({}) outputs.", + "Encumber coin split transaction (tx_id={}) outputs", tx_id ); - if input_selection.requires_change_output() { - let unblinded_output = stp.get_change_unblinded_output()?.ok_or_else(|| { + // again, to obtain output for leftover change + if has_leftover_change { + // obtaining output for the `change` + let unblinded_output_for_change = stp.get_change_unblinded_output()?.ok_or_else(|| { OutputManagerError::BuildError( - "There should be a change output metadata signature available".to_string(), + "There should be a `change` output metadata signature available".to_string(), ) })?; - outputs.push(DbUnblindedOutput::rewindable_from_unblinded_output( - unblinded_output, + + // appending `change` output to the result + dest_outputs.push(DbUnblindedOutput::rewindable_from_unblinded_output( + unblinded_output_for_change, &self.resources.factories, &self.resources.rewind_data.clone(), None, @@ -1639,19 +2192,188 @@ where )?); } + // encumbering transaction self.resources .db - .encumber_outputs(tx_id, input_selection.into_selected(), outputs)?; + .encumber_outputs(tx_id, src_outputs.clone(), dest_outputs)?; self.confirm_encumberance(tx_id)?; - trace!(target: LOG_TARGET, "Finalize coin split transaction ({}).", tx_id); + + trace!( + target: LOG_TARGET, + "finalizing coin split transaction (tx_id={}).", + tx_id + ); + + // finalizing transaction stp.finalize( KernelFeatures::empty(), - &factories, + &self.resources.factories, None, self.last_seen_tip_height.unwrap_or(u64::MAX), )?; - let tx = stp.take_transaction()?; - Ok((tx_id, tx, utxos_total_value)) + + let value = if has_leftover_change { + total_split_amount + } else { + total_split_amount + final_fee + }; + + Ok((tx_id, stp.take_transaction()?, value)) + } + + #[allow(clippy::too_many_lines)] + pub async fn create_coin_join( + &mut self, + commitments: Vec, + fee_per_gram: MicroTari, + ) -> Result<(TxId, Transaction, MicroTari), OutputManagerError> { + let covenant = Covenant::default(); + let noop_script = script!(Nop); + let default_metadata_size = self.default_metadata_size(); + + let src_outputs = self.resources.db.fetch_unspent_outputs_for_spending( + UtxoSelectionCriteria::specific(commitments), + MicroTari::zero(), + None, + )?; + + let accumulated_amount = src_outputs + .iter() + .fold(MicroTari::zero(), |acc, x| acc + x.unblinded_output.value); + + let fee = self + .get_fee_calc() + .calculate(fee_per_gram, 1, src_outputs.len(), 1, default_metadata_size); + + let aftertax_amount = accumulated_amount.saturating_sub(fee); + + // checking, again, whether a total output value is enough + if aftertax_amount == MicroTari::zero() { + error!(target: LOG_TARGET, "failed to join coins, not enough funds"); + return Err(OutputManagerError::NotEnoughFunds); + } + + // preliminary balance check + if self.get_balance(None)?.available_balance < aftertax_amount { + return Err(OutputManagerError::NotEnoughFunds); + } + + // ---------------------------------------------------------------------------- + // initializing new transaction + + trace!(target: LOG_TARGET, "initializing new join transaction"); + + let mut tx_builder = SenderTransactionProtocol::builder(0, self.resources.consensus_constants.clone()); + tx_builder + .with_lock_height(0) + .with_fee_per_gram(fee_per_gram) + .with_offset(PrivateKey::random(&mut OsRng)) + .with_private_nonce(PrivateKey::random(&mut OsRng)) + .with_rewindable_outputs(self.resources.rewind_data.clone()); + + // collecting inputs from source outputs + let inputs: Vec = src_outputs + .iter() + .map(|src_out| { + src_out + .unblinded_output + .as_transaction_input(&self.resources.factories.commitment) + }) + .try_collect()?; + + // adding inputs to the transaction + src_outputs.iter().zip(inputs).for_each(|(src_output, input)| { + trace!( + target: LOG_TARGET, + "adding transaction input: output_hash=: {:?}", + src_output.hash + ); + tx_builder.with_input(input, src_output.unblinded_output.clone()); + }); + + // initializing primary output + let (spending_key, script_private_key) = self.get_spend_and_script_keys().await?; + let output_features = OutputFeatures { + recovery_byte: self.calculate_recovery_byte(spending_key.clone(), accumulated_amount.as_u64(), true)?, + ..Default::default() + }; + + // generating sender's keypair + let sender_offset_private_key = PrivateKey::random(&mut OsRng); + let sender_offset_public_key = PublicKey::from_secret_key(&sender_offset_private_key); + + let commitment_signature = TransactionOutput::create_final_metadata_signature( + TransactionOutputVersion::get_current_version(), + aftertax_amount, + &spending_key, + &noop_script, + &output_features, + &sender_offset_private_key, + &covenant, + )?; + + let output = DbUnblindedOutput::rewindable_from_unblinded_output( + UnblindedOutput::new_current_version( + aftertax_amount, + spending_key, + output_features, + noop_script, + inputs!(PublicKey::from_secret_key(&script_private_key)), + script_private_key, + sender_offset_public_key, + commitment_signature, + 0, + covenant.clone(), + ), + &self.resources.factories, + &self.resources.rewind_data.clone(), + None, + None, + )?; + + tx_builder + .with_output(output.unblinded_output.clone(), sender_offset_private_key) + .map_err(|e| OutputManagerError::BuildError(e.message))?; + + let mut stp = tx_builder + .build::( + &self.resources.factories, + None, + self.last_seen_tip_height.unwrap_or(u64::MAX), + ) + .map_err(|e| OutputManagerError::BuildError(e.message))?; + + // The Transaction Protocol built successfully so we will pull the unspent outputs out of the unspent list and + // store them until the transaction times out OR is confirmed + let tx_id = stp.get_tx_id()?; + + trace!( + target: LOG_TARGET, + "Encumber coin join transaction (tx_id={}) outputs", + tx_id + ); + + // encumbering transaction + self.resources + .db + .encumber_outputs(tx_id, src_outputs.clone(), vec![output])?; + self.confirm_encumberance(tx_id)?; + + trace!( + target: LOG_TARGET, + "finalizing coin join transaction (tx_id={}).", + tx_id + ); + + // finalizing transaction + stp.finalize( + KernelFeatures::empty(), + &self.resources.factories, + None, + self.last_seen_tip_height.unwrap_or(u64::MAX), + )?; + + Ok((tx_id, stp.take_transaction()?, aftertax_amount + fee)) } async fn fetch_outputs_from_node( @@ -1839,7 +2561,6 @@ where ); let factories = CryptoFactories::default(); - println!("he`"); let mut stp = builder .build::( &self.resources.factories, @@ -2051,6 +2772,7 @@ struct UtxoSelection { fee_with_change: MicroTari, } +#[allow(dead_code)] impl UtxoSelection { pub fn as_final_fee(&self) -> MicroTari { if self.requires_change_output { diff --git a/base_layer/wallet/src/output_manager_service/storage/database/backend.rs b/base_layer/wallet/src/output_manager_service/storage/database/backend.rs index 70bd6e3ada..4527e1b561 100644 --- a/base_layer/wallet/src/output_manager_service/storage/database/backend.rs +++ b/base_layer/wallet/src/output_manager_service/storage/database/backend.rs @@ -49,6 +49,7 @@ pub trait OutputManagerBackend: Send + Sync + Clone { mined_in_block: Vec, mmr_position: u64, confirmed: bool, + mined_timestamp: u64, ) -> Result<(), OutputManagerStorageError>; fn set_output_to_unmined(&self, hash: Vec) -> Result<(), OutputManagerStorageError>; diff --git a/base_layer/wallet/src/output_manager_service/storage/database/mod.rs b/base_layer/wallet/src/output_manager_service/storage/database/mod.rs index ed37283cd7..91c15e1bbb 100644 --- a/base_layer/wallet/src/output_manager_service/storage/database/mod.rs +++ b/base_layer/wallet/src/output_manager_service/storage/database/mod.rs @@ -61,6 +61,7 @@ pub enum SortDirection { pub struct OutputBackendQuery { pub tip_height: i64, pub status: Vec, + pub commitments: Vec, pub pagination: Option<(i64, i64)>, pub value_min: Option<(i64, bool)>, pub value_max: Option<(i64, bool)>, @@ -72,6 +73,7 @@ impl Default for OutputBackendQuery { Self { tip_height: i64::MAX, status: vec![OutputStatus::Spent], + commitments: vec![], pagination: None, value_min: None, value_max: None, @@ -403,9 +405,17 @@ where T: OutputManagerBackend + 'static mined_in_block: HashOutput, mmr_position: u64, confirmed: bool, + mined_timestamp: u64, ) -> Result<(), OutputManagerStorageError> { let db = self.db.clone(); - db.set_received_output_mined_height(hash, mined_height, mined_in_block, mmr_position, confirmed)?; + db.set_received_output_mined_height( + hash, + mined_height, + mined_in_block, + mmr_position, + confirmed, + mined_timestamp, + )?; Ok(()) } diff --git a/base_layer/wallet/src/output_manager_service/storage/models.rs b/base_layer/wallet/src/output_manager_service/storage/models.rs index e21d955ab9..839da3d3de 100644 --- a/base_layer/wallet/src/output_manager_service/storage/models.rs +++ b/base_layer/wallet/src/output_manager_service/storage/models.rs @@ -22,6 +22,7 @@ use std::cmp::Ordering; +use chrono::NaiveDateTime; use derivative::Derivative; use tari_common_types::types::{BlockHash, BulletRangeProof, Commitment, HashOutput, PrivateKey}; use tari_core::transactions::{ @@ -43,6 +44,7 @@ pub struct DbUnblindedOutput { pub mined_height: Option, pub mined_in_block: Option, pub mined_mmr_position: Option, + pub mined_timestamp: Option, pub marked_deleted_at_height: Option, pub marked_deleted_in_block: Option, pub spending_priority: SpendingPriority, @@ -63,6 +65,7 @@ impl DbUnblindedOutput { mined_height: None, mined_in_block: None, mined_mmr_position: None, + mined_timestamp: None, marked_deleted_at_height: None, marked_deleted_in_block: None, spending_priority: spend_priority.unwrap_or(SpendingPriority::Normal), @@ -85,6 +88,7 @@ impl DbUnblindedOutput { mined_height: None, mined_in_block: None, mined_mmr_position: None, + mined_timestamp: None, marked_deleted_at_height: None, marked_deleted_in_block: None, spending_priority: spending_priority.unwrap_or(SpendingPriority::Normal), diff --git a/base_layer/wallet/src/output_manager_service/storage/sqlite_db/mod.rs b/base_layer/wallet/src/output_manager_service/storage/sqlite_db/mod.rs index a74cd432c6..09fdafe15d 100644 --- a/base_layer/wallet/src/output_manager_service/storage/sqlite_db/mod.rs +++ b/base_layer/wallet/src/output_manager_service/storage/sqlite_db/mod.rs @@ -26,6 +26,7 @@ use std::{ }; use aes_gcm::Aes256Gcm; +use chrono::NaiveDateTime; use derivative::Derivative; use diesel::{prelude::*, result::Error as DieselError, SqliteConnection}; use log::*; @@ -481,6 +482,7 @@ impl OutputManagerBackend for OutputManagerSqliteDatabase { mined_in_block: Vec, mmr_position: u64, confirmed: bool, + mined_timestamp: u64, ) -> Result<(), OutputManagerStorageError> { let start = Instant::now(); let conn = self.database_connection.get_pooled_connection()?; @@ -501,6 +503,7 @@ impl OutputManagerBackend for OutputManagerSqliteDatabase { outputs::mined_in_block.eq(mined_in_block), outputs::mined_mmr_position.eq(mmr_position as i64), outputs::status.eq(status), + outputs::mined_timestamp.eq(NaiveDateTime::from_timestamp(mined_timestamp as i64, 0)), )) .execute(&conn) .num_rows_affected_or_not_found(1)?; @@ -528,6 +531,7 @@ impl OutputManagerBackend for OutputManagerSqliteDatabase { outputs::mined_in_block.eq::>>(None), outputs::mined_mmr_position.eq::>(None), outputs::status.eq(OutputStatus::Invalid as i32), + outputs::mined_timestamp.eq::>(None), )) .execute(&conn) .num_rows_affected_or_not_found(1)?; @@ -554,6 +558,7 @@ impl OutputManagerBackend for OutputManagerSqliteDatabase { outputs::mined_height.eq::>(None), outputs::mined_in_block.eq::>>(None), outputs::mined_mmr_position.eq::>(None), + outputs::mined_timestamp.eq::>(None), )) .execute(&conn)?; diff --git a/base_layer/wallet/src/output_manager_service/storage/sqlite_db/output_sql.rs b/base_layer/wallet/src/output_manager_service/storage/sqlite_db/output_sql.rs index 4640ad39ae..753c5c941e 100644 --- a/base_layer/wallet/src/output_manager_service/storage/sqlite_db/output_sql.rs +++ b/base_layer/wallet/src/output_manager_service/storage/sqlite_db/output_sql.rs @@ -23,6 +23,7 @@ use std::convert::{TryFrom, TryInto}; use aes_gcm::Aes256Gcm; +use chrono::NaiveDateTime; use derivative::Derivative; use diesel::{prelude::*, sql_query, SqliteConnection}; use log::*; @@ -101,6 +102,7 @@ pub struct OutputSql { pub features_json: String, pub spending_priority: i32, pub covenant: Vec, + pub mined_timestamp: Option, pub encrypted_value: Vec, pub contract_id: Option>, pub minimum_value_promise: i64, @@ -145,6 +147,17 @@ impl OutputSql { _ => query.filter(outputs::status.eq_any::>(q.status.into_iter().map(|s| s as i32).collect())), }; + // filtering by Commitment + if !q.commitments.is_empty() { + query = match q.commitments.len() { + 0 => query, + 1 => query.filter(outputs::commitment.eq(q.commitments[0].to_vec())), + _ => query.filter( + outputs::commitment.eq_any::>>(q.commitments.into_iter().map(|c| c.to_vec()).collect()), + ), + }; + } + // if set, filtering by minimum value if let Some((min, is_inclusive)) = q.value_min { query = if is_inclusive { @@ -209,6 +222,16 @@ impl OutputSql { .filter(outputs::features_unique_id.eq(unique_id)) .filter(outputs::features_parent_public_key.eq(parent_public_key.as_ref().map(|pk| pk.to_vec()))); }, + UtxoSelectionFilter::SpecificOutputs { commitments } => { + query = match commitments.len() { + 0 => query, + 1 => query.filter(outputs::commitment.eq(commitments[0].to_vec())), + _ => query.filter( + outputs::commitment + .eq_any::>>(commitments.into_iter().map(|c| c.to_vec()).collect()), + ), + }; + }, UtxoSelectionFilter::ContractOutput { contract_id, output_type, @@ -217,9 +240,6 @@ impl OutputSql { .filter(outputs::contract_id.eq(contract_id.as_slice())) .filter(outputs::output_type.eq(i32::from(output_type.as_byte()))); }, - UtxoSelectionFilter::SpecificOutputs { outputs } => { - query = query.filter(outputs::hash.eq_any(outputs.iter().map(|o| &o.hash))) - }, } for exclude in &selection_criteria.excluding { @@ -718,6 +738,7 @@ impl TryFrom for DbUnblindedOutput { mined_height: o.mined_height.map(|mh| mh as u64), mined_in_block: o.mined_in_block, mined_mmr_position: o.mined_mmr_position.map(|mp| mp as u64), + mined_timestamp: o.mined_timestamp, marked_deleted_at_height: o.marked_deleted_at_height.map(|d| d as u64), marked_deleted_in_block: o.marked_deleted_in_block, spending_priority, diff --git a/base_layer/wallet/src/output_manager_service/tasks/txo_validation_task.rs b/base_layer/wallet/src/output_manager_service/tasks/txo_validation_task.rs index 7ae2648843..f14f1aa1eb 100644 --- a/base_layer/wallet/src/output_manager_service/tasks/txo_validation_task.rs +++ b/base_layer/wallet/src/output_manager_service/tasks/txo_validation_task.rs @@ -234,7 +234,7 @@ where unmined.len(), self.operation_id ); - for (output, mined_height, mined_in_block, mmr_position) in &mined { + for (output, mined_height, mined_in_block, mmr_position, mined_timestamp) in &mined { info!( target: LOG_TARGET, "Updating output comm:{}: hash {} as mined at height {} with current tip at {} (Operation ID: {})", @@ -244,8 +244,15 @@ where tip_height, self.operation_id ); - self.update_output_as_mined(output, mined_in_block, *mined_height, *mmr_position, tip_height) - .await?; + self.update_output_as_mined( + output, + mined_in_block, + *mined_height, + *mmr_position, + tip_height, + *mined_timestamp, + ) + .await?; } } @@ -383,7 +390,7 @@ where base_node_client: &mut BaseNodeWalletRpcClient, ) -> Result< ( - Vec<(DbUnblindedOutput, u64, BlockHash, u64)>, + Vec<(DbUnblindedOutput, u64, BlockHash, u64, u64)>, Vec, u64, ), @@ -412,6 +419,7 @@ where returned_output.mined_height, returned_output.mined_in_block.clone(), returned_output.mmr_position, + returned_output.mined_timestamp, )) } else { unmined.push(output.clone()); @@ -429,6 +437,7 @@ where mined_height: u64, mmr_position: u64, tip_height: u64, + mined_timestamp: u64, ) -> Result<(), OutputManagerProtocolError> { let confirmed = (tip_height - mined_height) >= self.config.num_confirmations_required; @@ -439,6 +448,7 @@ where mined_in_block.clone(), mmr_position, confirmed, + mined_timestamp, ) .for_protocol(self.operation_id)?; diff --git a/base_layer/wallet/src/schema.rs b/base_layer/wallet/src/schema.rs index 72c8b97dda..9d03a623d2 100644 --- a/base_layer/wallet/src/schema.rs +++ b/base_layer/wallet/src/schema.rs @@ -46,6 +46,7 @@ table! { confirmations -> Nullable, mined_height -> Nullable, mined_in_block -> Nullable, + mined_timestamp -> Nullable, transaction_signature_nonce -> Binary, transaction_signature_key -> Binary, } @@ -153,6 +154,7 @@ table! { features_json -> Text, spending_priority -> Integer, covenant -> Binary, + mined_timestamp -> Nullable, encrypted_value -> Binary, contract_id -> Nullable, minimum_value_promise -> BigInt, diff --git a/base_layer/wallet/src/transaction_service/handle.rs b/base_layer/wallet/src/transaction_service/handle.rs index e7cd6980cf..bd6a341d1a 100644 --- a/base_layer/wallet/src/transaction_service/handle.rs +++ b/base_layer/wallet/src/transaction_service/handle.rs @@ -28,6 +28,7 @@ use std::{ }; use aes_gcm::Aes256Gcm; +use chrono::NaiveDateTime; use tari_common_types::{ transaction::{ImportStatus, TxId}, types::PublicKey, @@ -103,6 +104,7 @@ pub enum TransactionServiceRequest { import_status: ImportStatus, tx_id: Option, current_height: Option, + mined_timestamp: Option, }, SubmitTransactionToSelf(TxId, Transaction, MicroTari, MicroTari, String), SetLowPowerMode, @@ -177,8 +179,9 @@ impl fmt::Display for TransactionServiceRequest { import_status, tx_id, current_height, + mined_timestamp, } => f.write_str(&format!( - "ImportUtxo (from {}, {}, {} with maturity {} and {:?} and {:?} and {:?})", + "ImportUtxo (from {}, {}, {} with maturity {} and {:?} and {:?} and {:?} and {:?})", source_public_key, amount, message, @@ -186,6 +189,7 @@ impl fmt::Display for TransactionServiceRequest { import_status, tx_id, current_height, + mined_timestamp )), Self::SubmitTransactionToSelf(tx_id, _, _, _, _) => f.write_str(&format!("SubmitTransaction ({})", tx_id)), Self::SetLowPowerMode => f.write_str("SetLowPowerMode "), @@ -257,6 +261,7 @@ impl Display for TransactionSendStatus { #[derive(Clone, Debug, Hash, PartialEq, Eq)] pub enum TransactionEvent { MempoolBroadcastTimedOut(TxId), + NewBlockMined(TxId), ReceivedTransaction(TxId), ReceivedTransactionReply(TxId), ReceivedFinalizedTransaction(TxId), @@ -367,6 +372,9 @@ impl fmt::Display for TransactionEvent { TransactionEvent::TransactionValidationFailed(operation_id) => { write!(f, "Transaction validation failed: {}", operation_id) }, + TransactionEvent::NewBlockMined(tx_id) => { + write!(f, "New block mined {}", tx_id) + }, } } } @@ -608,6 +616,7 @@ impl TransactionServiceHandle { import_status: ImportStatus, tx_id: Option, current_height: Option, + mined_timestamp: Option, ) -> Result { match self .handle @@ -619,6 +628,7 @@ impl TransactionServiceHandle { import_status, tx_id, current_height, + mined_timestamp, }) .await?? { diff --git a/base_layer/wallet/src/transaction_service/protocols/transaction_receive_protocol.rs b/base_layer/wallet/src/transaction_service/protocols/transaction_receive_protocol.rs index d0b1c3c5d3..d133036009 100644 --- a/base_layer/wallet/src/transaction_service/protocols/transaction_receive_protocol.rs +++ b/base_layer/wallet/src/transaction_service/protocols/transaction_receive_protocol.rs @@ -452,6 +452,7 @@ where TransactionDirection::Inbound, None, None, + None, ); self.resources diff --git a/base_layer/wallet/src/transaction_service/protocols/transaction_send_protocol.rs b/base_layer/wallet/src/transaction_service/protocols/transaction_send_protocol.rs index ebb3597de1..c43e00e7b0 100644 --- a/base_layer/wallet/src/transaction_service/protocols/transaction_send_protocol.rs +++ b/base_layer/wallet/src/transaction_service/protocols/transaction_send_protocol.rs @@ -585,6 +585,7 @@ where TransactionDirection::Outbound, None, None, + None, ); self.resources diff --git a/base_layer/wallet/src/transaction_service/protocols/transaction_validation_protocol.rs b/base_layer/wallet/src/transaction_service/protocols/transaction_validation_protocol.rs index 3fed88eb5f..34bffb695a 100644 --- a/base_layer/wallet/src/transaction_service/protocols/transaction_validation_protocol.rs +++ b/base_layer/wallet/src/transaction_service/protocols/transaction_validation_protocol.rs @@ -129,7 +129,7 @@ where unmined.len(), self.operation_id ); - for (mined_tx, mined_height, mined_in_block, num_confirmations) in &mined { + for (mined_tx, mined_height, mined_in_block, num_confirmations, mined_timestamp) in &mined { debug!( target: LOG_TARGET, "Updating transaction {} as mined and confirmed '{}' (Operation ID: {})", @@ -143,11 +143,12 @@ where mined_in_block, *mined_height, *num_confirmations, + *mined_timestamp, ) .await?; state_changed = true; } - if let Some((tip_height, tip_block)) = tip_info { + if let Some((tip_height, tip_block, tip_mined_timestamp)) = tip_info { for unmined_tx in &unmined { // Treat coinbases separately if unmined_tx.is_coinbase() { @@ -162,6 +163,7 @@ where unmined_tx.tx_id, &tip_block, tip_height, + tip_mined_timestamp, tip_height.saturating_sub(unmined_tx.coinbase_block_height.unwrap_or_default()), ) .await?; @@ -183,7 +185,7 @@ where ); self.update_transaction_as_unmined(unmined_tx.tx_id, &unmined_tx.status) .await?; - state_changed = true; + self.publish_event(TransactionEvent::NewBlockMined(unmined_tx.tx_id)); } } } @@ -276,9 +278,9 @@ where base_node_client: &mut BaseNodeWalletRpcClient, ) -> Result< ( - Vec<(UnconfirmedTransactionInfo, u64, BlockHash, u64)>, + Vec<(UnconfirmedTransactionInfo, u64, BlockHash, u64, u64)>, Vec, - Option<(u64, BlockHash)>, + Option<(u64, BlockHash, u64)>, ), TransactionServiceError, > { @@ -328,6 +330,7 @@ where response.block_height, response.block_hash.unwrap(), response.confirmations, + response.mined_timestamp.unwrap(), )); } else { warn!( @@ -350,6 +353,9 @@ where batch_response.tip_hash.ok_or_else(|| { TransactionServiceError::ProtobufConversionError("Missing `tip_hash` field".to_string()) })?, + batch_response.tip_mined_timestamp.ok_or_else(|| { + TransactionServiceError::ProtobufConversionError("Missing `tip_hash` field".to_string()) + })?, )), )) } @@ -395,12 +401,14 @@ where mined_in_block: &BlockHash, mined_height: u64, num_confirmations: u64, + mined_timestamp: u64, ) -> Result<(), TransactionServiceProtocolError> { self.db .set_transaction_mined_height( tx_id, mined_height, mined_in_block.clone(), + mined_timestamp, num_confirmations, num_confirmations >= self.config.num_confirmations_required, status.is_faux(), @@ -449,6 +457,7 @@ where tx_id: TxId, mined_in_block: &BlockHash, mined_height: u64, + mined_timestamp: u64, num_confirmations: u64, ) -> Result<(), TransactionServiceProtocolError> { self.db @@ -456,6 +465,7 @@ where tx_id, mined_height, mined_in_block.clone(), + mined_timestamp, num_confirmations, num_confirmations >= self.config.num_confirmations_required, false, diff --git a/base_layer/wallet/src/transaction_service/service.rs b/base_layer/wallet/src/transaction_service/service.rs index 872aba1bbd..2fca954d63 100644 --- a/base_layer/wallet/src/transaction_service/service.rs +++ b/base_layer/wallet/src/transaction_service/service.rs @@ -686,6 +686,7 @@ where import_status, tx_id, current_height, + mined_timestamp, } => self .add_utxo_import_transaction_with_status( amount, @@ -695,6 +696,7 @@ where import_status, tx_id, current_height, + mined_timestamp, ) .await .map(TransactionServiceResponse::UtxoImported), @@ -926,6 +928,7 @@ where TransactionDirection::Inbound, None, None, + None, ), ) .await?; @@ -1146,6 +1149,7 @@ where TransactionDirection::Outbound, None, None, + None, ), ) .await?; @@ -1276,6 +1280,7 @@ where TransactionDirection::Outbound, None, None, + None, ), ) .await?; @@ -2263,6 +2268,7 @@ where import_status: ImportStatus, tx_id: Option, current_height: Option, + mined_timestamp: Option, ) -> Result { let tx_id = if let Some(id) = tx_id { id } else { TxId::new_random() }; self.db @@ -2275,6 +2281,7 @@ where maturity, import_status.clone(), current_height, + mined_timestamp, ) .await?; let transaction_event = match import_status { @@ -2354,6 +2361,7 @@ where TransactionDirection::Inbound, None, None, + None, ), ) .await?; @@ -2415,6 +2423,7 @@ where TransactionDirection::Inbound, Some(block_height), None, + None, ), ) .await?; diff --git a/base_layer/wallet/src/transaction_service/storage/database.rs b/base_layer/wallet/src/transaction_service/storage/database.rs index ba5be1a2d7..5b157a2e79 100644 --- a/base_layer/wallet/src/transaction_service/storage/database.rs +++ b/base_layer/wallet/src/transaction_service/storage/database.rs @@ -29,7 +29,7 @@ use std::{ }; use aes_gcm::Aes256Gcm; -use chrono::Utc; +use chrono::{NaiveDateTime, Utc}; use log::*; use tari_common_types::{ transaction::{ImportStatus, TransactionDirection, TransactionStatus, TxId}, @@ -140,6 +140,7 @@ pub trait TransactionBackend: Send + Sync + Clone { tx_id: TxId, mined_height: u64, mined_in_block: BlockHash, + mined_timestamp: u64, num_confirmations: u64, is_confirmed: bool, is_faux: bool, @@ -765,6 +766,7 @@ where T: TransactionBackend + 'static maturity: Option, import_status: ImportStatus, current_height: Option, + mined_timestamp: Option, ) -> Result<(), TransactionStorageError> { let transaction = CompletedTransaction::new( tx_id, @@ -785,6 +787,7 @@ where T: TransactionBackend + 'static TransactionDirection::Inbound, maturity, current_height, + mined_timestamp, ); let db_clone = self.db.clone(); @@ -869,6 +872,7 @@ where T: TransactionBackend + 'static tx_id: TxId, mined_height: u64, mined_in_block: BlockHash, + mined_timestamp: u64, num_confirmations: u64, is_confirmed: bool, is_faux: bool, @@ -879,6 +883,7 @@ where T: TransactionBackend + 'static tx_id, mined_height, mined_in_block, + mined_timestamp, num_confirmations, is_confirmed, is_faux, diff --git a/base_layer/wallet/src/transaction_service/storage/models.rs b/base_layer/wallet/src/transaction_service/storage/models.rs index 38d835ee23..8c8932f062 100644 --- a/base_layer/wallet/src/transaction_service/storage/models.rs +++ b/base_layer/wallet/src/transaction_service/storage/models.rs @@ -146,6 +146,7 @@ pub struct CompletedTransaction { pub confirmations: Option, pub mined_height: Option, pub mined_in_block: Option, + pub mined_timestamp: Option, } impl CompletedTransaction { @@ -162,6 +163,7 @@ impl CompletedTransaction { direction: TransactionDirection, coinbase_block_height: Option, mined_height: Option, + mined_timestamp: Option, ) -> Self { let transaction_signature = if let Some(excess_sig) = transaction.first_kernel_excess_sig() { excess_sig.clone() @@ -187,6 +189,7 @@ impl CompletedTransaction { confirmations: None, mined_height, mined_in_block: None, + mined_timestamp, } } @@ -291,6 +294,7 @@ impl From for CompletedTransaction { confirmations: None, mined_height: None, mined_in_block: None, + mined_timestamp: None, } } } @@ -320,6 +324,7 @@ impl From for CompletedTransaction { confirmations: None, mined_height: None, mined_in_block: None, + mined_timestamp: None, } } } diff --git a/base_layer/wallet/src/transaction_service/storage/sqlite_db.rs b/base_layer/wallet/src/transaction_service/storage/sqlite_db.rs index 754ab3bd62..17972e34a1 100644 --- a/base_layer/wallet/src/transaction_service/storage/sqlite_db.rs +++ b/base_layer/wallet/src/transaction_service/storage/sqlite_db.rs @@ -1017,6 +1017,7 @@ impl TransactionBackend for TransactionServiceSqliteDatabase { tx_id: TxId, mined_height: u64, mined_in_block: BlockHash, + mined_timestamp: u64, num_confirmations: u64, is_confirmed: bool, is_faux: bool, @@ -1029,6 +1030,7 @@ impl TransactionBackend for TransactionServiceSqliteDatabase { v.update_mined_height( mined_height, mined_in_block, + mined_timestamp, num_confirmations, is_confirmed, &conn, @@ -1702,6 +1704,7 @@ struct CompletedTransactionSql { confirmations: Option, mined_height: Option, mined_in_block: Option>, + mined_timestamp: Option, transaction_signature_nonce: Vec, transaction_signature_key: Vec, } @@ -1901,6 +1904,7 @@ impl CompletedTransactionSql { &self, mined_height: u64, mined_in_block: BlockHash, + mined_timestamp: u64, num_confirmations: u64, is_confirmed: bool, conn: &SqliteConnection, @@ -1924,6 +1928,7 @@ impl CompletedTransactionSql { status: Some(status), mined_height: Some(Some(mined_height as i64)), mined_in_block: Some(Some(mined_in_block)), + mined_timestamp: Some(NaiveDateTime::from_timestamp(mined_timestamp as i64, 0)), // If the tx is mined, then it can't be cancelled cancelled: None, ..Default::default() @@ -1976,6 +1981,7 @@ impl TryFrom for CompletedTransactionSql { confirmations: c.confirmations.map(|ic| ic as i64), mined_height: c.mined_height.map(|ic| ic as i64), mined_in_block: c.mined_in_block, + mined_timestamp: c.mined_timestamp, transaction_signature_nonce: c.transaction_signature.get_public_nonce().to_vec(), transaction_signature_key: c.transaction_signature.get_signature().to_vec(), }) @@ -2027,6 +2033,7 @@ impl TryFrom for CompletedTransaction { confirmations: c.confirmations.map(|ic| ic as u64), mined_height: c.mined_height.map(|ic| ic as u64), mined_in_block: c.mined_in_block, + mined_timestamp: c.mined_timestamp, }) } } @@ -2044,6 +2051,7 @@ pub struct UpdateCompletedTransactionSql { confirmations: Option>, mined_height: Option>, mined_in_block: Option>>, + mined_timestamp: Option, transaction_signature_nonce: Option>, transaction_signature_key: Option>, } @@ -2356,6 +2364,7 @@ mod test { confirmations: None, mined_height: None, mined_in_block: None, + mined_timestamp: None, }; let completed_tx2 = CompletedTransaction { tx_id: 3u64.into(), @@ -2376,6 +2385,7 @@ mod test { confirmations: None, mined_height: None, mined_in_block: None, + mined_timestamp: None, }; CompletedTransactionSql::try_from(completed_tx1.clone()) @@ -2506,6 +2516,7 @@ mod test { confirmations: None, mined_height: None, mined_in_block: None, + mined_timestamp: None, }; let coinbase_tx2 = CompletedTransaction { @@ -2527,6 +2538,7 @@ mod test { confirmations: None, mined_height: None, mined_in_block: None, + mined_timestamp: None, }; let coinbase_tx3 = CompletedTransaction { @@ -2548,6 +2560,7 @@ mod test { confirmations: None, mined_height: None, mined_in_block: None, + mined_timestamp: None, }; CompletedTransactionSql::try_from(coinbase_tx1) @@ -2659,6 +2672,7 @@ mod test { confirmations: None, mined_height: None, mined_in_block: None, + mined_timestamp: None, }; let mut completed_tx_sql = CompletedTransactionSql::try_from(completed_tx.clone()).unwrap(); @@ -2749,6 +2763,7 @@ mod test { confirmations: None, mined_height: None, mined_in_block: None, + mined_timestamp: None, }; let completed_tx_sql = CompletedTransactionSql::try_from(completed_tx).unwrap(); completed_tx_sql.commit(&conn).unwrap(); @@ -2874,6 +2889,7 @@ mod test { confirmations: None, mined_height: None, mined_in_block: None, + mined_timestamp: None, }; let completed_tx_sql = CompletedTransactionSql::try_from(completed_tx.clone()).unwrap(); completed_tx_sql.commit(&conn).unwrap(); diff --git a/base_layer/wallet/src/transaction_service/tasks/check_faux_transaction_status.rs b/base_layer/wallet/src/transaction_service/tasks/check_faux_transaction_status.rs index 07c9ead31b..d4d2ca8a0f 100644 --- a/base_layer/wallet/src/transaction_service/tasks/check_faux_transaction_status.rs +++ b/base_layer/wallet/src/transaction_service/tasks/check_faux_transaction_status.rs @@ -136,6 +136,7 @@ pub async fn check_faux_transactions( tx.tx_id, mined_height, mined_in_block, + 0, num_confirmations, is_confirmed, is_valid, diff --git a/base_layer/wallet/src/utxo_scanner_service/utxo_scanner_task.rs b/base_layer/wallet/src/utxo_scanner_service/utxo_scanner_task.rs index 4391fceeac..489790fdba 100644 --- a/base_layer/wallet/src/utxo_scanner_service/utxo_scanner_task.rs +++ b/base_layer/wallet/src/utxo_scanner_service/utxo_scanner_task.rs @@ -25,7 +25,7 @@ use std::{ time::{Duration, Instant}, }; -use chrono::Utc; +use chrono::{NaiveDateTime, Utc}; use futures::StreamExt; use log::*; use tari_common_types::{ @@ -449,6 +449,7 @@ where TBackend: WalletBackend + 'static let response = response.map_err(|e| UtxoScannerError::RpcStatus(e.to_string()))?; let current_height = response.height; let current_header_hash = response.header_hash; + let mined_timestamp = NaiveDateTime::from_timestamp(response.mined_timestamp as i64, 0); let outputs = response .outputs .into_iter() @@ -462,7 +463,7 @@ where TBackend: WalletBackend + 'static scan_for_outputs_profiling.push(start.elapsed()); let (count, amount) = self - .import_utxos_to_transaction_service(found_outputs, current_height) + .import_utxos_to_transaction_service(found_outputs, current_height, mined_timestamp) .await?; self.resources @@ -559,6 +560,7 @@ where TBackend: WalletBackend + 'static &mut self, utxos: Vec<(UnblindedOutput, String, ImportStatus, TxId)>, current_height: u64, + mined_timestamp: NaiveDateTime, ) -> Result<(u64, MicroTari), UtxoScannerError> { let mut num_recovered = 0u64; let mut total_amount = MicroTari::from(0); @@ -575,6 +577,7 @@ where TBackend: WalletBackend + 'static import_status, tx_id, current_height, + mined_timestamp, ) .await { @@ -634,6 +637,7 @@ where TBackend: WalletBackend + 'static import_status: ImportStatus, tx_id: TxId, current_height: u64, + mined_timestamp: NaiveDateTime, ) -> Result { let tx_id = self .resources @@ -646,6 +650,7 @@ where TBackend: WalletBackend + 'static import_status, Some(tx_id), Some(current_height), + Some(mined_timestamp), ) .await?; diff --git a/base_layer/wallet/src/wallet.rs b/base_layer/wallet/src/wallet.rs index f5fdc613f8..ebc6801d1a 100644 --- a/base_layer/wallet/src/wallet.rs +++ b/base_layer/wallet/src/wallet.rs @@ -27,7 +27,7 @@ use log::*; use tari_common::configuration::bootstrap::ApplicationType; use tari_common_types::{ transaction::{ImportStatus, TxId}, - types::{ComSignature, PrivateKey, PublicKey}, + types::{ComSignature, Commitment, PrivateKey, PublicKey}, }; use tari_comms::{ multiaddr::Multiaddr, @@ -459,6 +459,7 @@ where ImportStatus::Imported, None, None, + None, ) .await?; @@ -501,6 +502,7 @@ where ImportStatus::Imported, None, None, + None, ) .await?; @@ -544,18 +546,99 @@ where signature.verify_challenge(&public_key, challenge.clone().as_slice()) } + /// Appraise the expected outputs and a fee + pub async fn preview_coin_split_with_commitments_no_amount( + &mut self, + commitments: Vec, + split_count: usize, + fee_per_gram: MicroTari, + ) -> Result<(Vec, MicroTari), WalletError> { + self.output_manager_service + .preview_coin_split_with_commitments_no_amount(commitments, split_count, fee_per_gram) + .await + .map_err(WalletError::OutputManagerError) + } + + /// Appraise the expected outputs and a fee + pub async fn preview_coin_join_with_commitments( + &mut self, + commitments: Vec, + fee_per_gram: MicroTari, + ) -> Result<(Vec, MicroTari), WalletError> { + self.output_manager_service + .preview_coin_join_with_commitments(commitments, fee_per_gram) + .await + .map_err(WalletError::OutputManagerError) + } + /// Do a coin split pub async fn coin_split( &mut self, + commitments: Vec, amount_per_split: MicroTari, split_count: usize, fee_per_gram: MicroTari, message: String, - lock_height: Option, ) -> Result { let coin_split_tx = self .output_manager_service - .create_coin_split(amount_per_split, split_count, fee_per_gram, lock_height) + .create_coin_split(commitments, amount_per_split, split_count, fee_per_gram) + .await; + + match coin_split_tx { + Ok((tx_id, split_tx, amount)) => { + let coin_tx = self + .transaction_service + .submit_transaction(tx_id, split_tx, amount, message) + .await; + match coin_tx { + Ok(_) => Ok(tx_id), + Err(e) => Err(WalletError::TransactionServiceError(e)), + } + }, + Err(e) => Err(WalletError::OutputManagerError(e)), + } + } + + /// Do a coin split + pub async fn coin_split_even( + &mut self, + commitments: Vec, + split_count: usize, + fee_per_gram: MicroTari, + message: String, + ) -> Result { + let coin_split_tx = self + .output_manager_service + .create_coin_split_even(commitments, split_count, fee_per_gram) + .await; + + match coin_split_tx { + Ok((tx_id, split_tx, amount)) => { + let coin_tx = self + .transaction_service + .submit_transaction(tx_id, split_tx, amount, message) + .await; + match coin_tx { + Ok(_) => Ok(tx_id), + Err(e) => Err(WalletError::TransactionServiceError(e)), + } + }, + Err(e) => Err(WalletError::OutputManagerError(e)), + } + } + + /// Do a coin split + pub async fn coin_split_even_with_commitments( + &mut self, + commitments: Vec, + split_count: usize, + fee_per_gram: MicroTari, + message: String, + ) -> Result { + let coin_split_tx = self + .output_manager_service + .create_coin_split_even(commitments, split_count, fee_per_gram) .await; match coin_split_tx { @@ -573,6 +656,33 @@ where } } + pub async fn coin_join( + &mut self, + commitments: Vec, + fee_per_gram: MicroTari, + msg: Option, + ) -> Result { + let coin_join_tx = self + .output_manager_service + .create_coin_join(commitments, fee_per_gram) + .await; + + match coin_join_tx { + Ok((tx_id, tx, output_value)) => { + let coin_tx = self + .transaction_service + .submit_transaction(tx_id, tx, output_value, msg.unwrap_or_default()) + .await; + + match coin_tx { + Ok(_) => Ok(tx_id), + Err(e) => Err(WalletError::TransactionServiceError(e)), + } + }, + Err(e) => Err(WalletError::OutputManagerError(e)), + } + } + /// Apply encryption to all the Wallet db backends. The Wallet backend will test if the db's are already encrypted /// in which case this will fail. pub async fn apply_encryption(&mut self, passphrase: String) -> Result<(), WalletError> { diff --git a/base_layer/wallet/tests/output_manager_service_tests/service.rs b/base_layer/wallet/tests/output_manager_service_tests/service.rs index 541ec47e39..32ab13f453 100644 --- a/base_layer/wallet/tests/output_manager_service_tests/service.rs +++ b/base_layer/wallet/tests/output_manager_service_tests/service.rs @@ -112,9 +112,8 @@ use crate::support::{ }; fn default_metadata_byte_size() -> usize { - let output_features = OutputFeatures { ..Default::default() }; TransactionWeight::latest().round_up_metadata_size( - output_features.consensus_encode_exact_size() + script![Nop].consensus_encode_exact_size(), + OutputFeatures::default().consensus_encode_exact_size() + script![Nop].consensus_encode_exact_size(), ) } @@ -411,6 +410,7 @@ async fn fee_estimate() { assert!(matches!(err, OutputManagerError::NotEnoughFunds)); } +#[ignore] #[allow(clippy::identity_op)] #[tokio::test] async fn test_utxo_selection_no_chain_metadata() { @@ -509,7 +509,7 @@ async fn test_utxo_selection_no_chain_metadata() { assert!(matches!(err, OutputManagerError::NotEnoughFunds)); // coin split uses the "Largest" selection strategy - let (_, tx, utxos_total_value) = oms.create_coin_split(amount, 5, fee_per_gram, None).await.unwrap(); + let (_, tx, utxos_total_value) = oms.create_coin_split(vec![], amount, 5, fee_per_gram).await.unwrap(); let expected_fee = fee_calc.calculate(fee_per_gram, 1, 1, 6, default_metadata_byte_size() * 6); assert_eq!(tx.body.get_total_fee(), expected_fee); assert_eq!(utxos_total_value, MicroTari::from(10_000)); @@ -527,6 +527,7 @@ async fn test_utxo_selection_no_chain_metadata() { #[tokio::test] #[allow(clippy::identity_op)] #[allow(clippy::too_many_lines)] +#[ignore] async fn test_utxo_selection_with_chain_metadata() { let factories = CryptoFactories::default(); let (connection, _tempdir) = get_temp_sqlite_database_connection(); @@ -594,7 +595,7 @@ async fn test_utxo_selection_with_chain_metadata() { assert!(matches!(err, OutputManagerError::NotEnoughFunds)); // test coin split is maturity aware - let (_, tx, utxos_total_value) = oms.create_coin_split(amount, 5, fee_per_gram, None).await.unwrap(); + let (_, tx, utxos_total_value) = oms.create_coin_split(vec![], amount, 5, fee_per_gram).await.unwrap(); assert_eq!(utxos_total_value, MicroTari::from(6_000)); let expected_fee = fee_calc.calculate(fee_per_gram, 1, 1, 6, default_metadata_byte_size() * 6); assert_eq!(tx.body.get_total_fee(), expected_fee); @@ -1166,7 +1167,8 @@ async fn sending_transaction_persisted_while_offline() { assert_eq!(balance.time_locked_balance.unwrap(), MicroTari::from(0)); assert_eq!(balance.pending_outgoing_balance, available_balance / 2); - // This simulates an offline wallet with a queued transaction that has not been sent to the receiving wallet yet + // This simulates an offline wallet with a queued transaction that has not been sent to the receiving wallet + // yet drop(oms.output_manager_handle); let mut oms = setup_output_manager_service(backend.clone(), ks_backend.clone(), true).await; @@ -1208,6 +1210,7 @@ async fn sending_transaction_persisted_while_offline() { } #[tokio::test] +#[ignore] async fn coin_split_with_change() { let factories = CryptoFactories::default(); let (connection, _tempdir) = get_temp_sqlite_database_connection(); @@ -1229,7 +1232,7 @@ async fn coin_split_with_change() { let split_count = 8; let (_tx_id, coin_split_tx, amount) = oms .output_manager_handle - .create_coin_split(1000.into(), split_count, fee_per_gram, None) + .create_coin_split(vec![], 1000.into(), split_count, fee_per_gram) .await .unwrap(); assert_eq!(coin_split_tx.body.inputs().len(), 2); @@ -1243,7 +1246,8 @@ async fn coin_split_with_change() { (split_count + 1) * default_metadata_byte_size(), ); assert_eq!(coin_split_tx.body.get_total_fee(), expected_fee); - assert_eq!(amount, val2 + val3); + // NOTE: assuming the LargestFirst strategy is used + assert_eq!(amount, val3); } #[tokio::test] @@ -1265,6 +1269,7 @@ async fn coin_split_no_change() { split_count, split_count * default_metadata_byte_size(), ); + let val1 = 4_000 * uT; let val2 = 5_000 * uT; let val3 = 6_000 * uT + expected_fee; @@ -1277,7 +1282,7 @@ async fn coin_split_no_change() { let (_tx_id, coin_split_tx, amount) = oms .output_manager_handle - .create_coin_split(1000.into(), split_count, fee_per_gram, None) + .create_coin_split(vec![], 1000.into(), split_count, fee_per_gram) .await .unwrap(); assert_eq!(coin_split_tx.body.inputs().len(), 3); @@ -1430,6 +1435,7 @@ async fn test_txo_validation() { mined_height: 1, mined_in_block: block1_header.hash(), output_hash: output1_tx_output.hash(), + mined_timestamp: 0, }, UtxoQueryResponse { output: Some(output2_tx_output.clone().into()), @@ -1437,6 +1443,7 @@ async fn test_txo_validation() { mined_height: 1, mined_in_block: block1_header.hash(), output_hash: output2_tx_output.hash(), + mined_timestamp: 0, }, ]; @@ -1545,10 +1552,10 @@ async fn test_txo_validation() { assert_eq!( balance.pending_incoming_balance, MicroTari::from(output1_value) - - MicroTari::from(900_000) - - MicroTari::from(1260) + //Output4 = output 1 -900_000 and 1260 for fees - MicroTari::from(8_000_000) + - MicroTari::from(16_000_000) + MicroTari::from(900_000) - + MicroTari::from(1260) + //Output4 = output 1 -900_000 and 1260 for fees + MicroTari::from(8_000_000) + + MicroTari::from(16_000_000) ); // Output 1: Spent in Block 5 - Unconfirmed @@ -1570,6 +1577,7 @@ async fn test_txo_validation() { mined_height: 1, mined_in_block: block1_header.hash(), output_hash: output1_tx_output.hash(), + mined_timestamp: 0, }, UtxoQueryResponse { output: Some(output2_tx_output.clone().into()), @@ -1577,6 +1585,7 @@ async fn test_txo_validation() { mined_height: 1, mined_in_block: block1_header.hash(), output_hash: output2_tx_output.hash(), + mined_timestamp: 0, }, UtxoQueryResponse { output: Some(output4_tx_output.clone().into()), @@ -1584,6 +1593,7 @@ async fn test_txo_validation() { mined_height: 5, mined_in_block: block5_header.hash(), output_hash: output4_tx_output.hash(), + mined_timestamp: 0, }, UtxoQueryResponse { output: Some(output5_tx_output.clone().into()), @@ -1591,6 +1601,7 @@ async fn test_txo_validation() { mined_height: 5, mined_in_block: block5_header.hash(), output_hash: output5_tx_output.hash(), + mined_timestamp: 0, }, UtxoQueryResponse { output: Some(output6_tx_output.clone().into()), @@ -1598,6 +1609,7 @@ async fn test_txo_validation() { mined_height: 5, mined_in_block: block5_header.hash(), output_hash: output6_tx_output.hash(), + mined_timestamp: 0, }, ]; @@ -1691,10 +1703,10 @@ async fn test_txo_validation() { assert_eq!( balance.available_balance, MicroTari::from(output2_value) + MicroTari::from(output3_value) + MicroTari::from(output1_value) - - MicroTari::from(900_000) - - MicroTari::from(1260) + //spent 900_000 and 1260 for fees - MicroTari::from(8_000_000) + //output 5 - MicroTari::from(16_000_000) // output 6 + MicroTari::from(900_000) - + MicroTari::from(1260) + //spent 900_000 and 1260 for fees + MicroTari::from(8_000_000) + //output 5 + MicroTari::from(16_000_000) // output 6 ); assert_eq!(balance.pending_outgoing_balance, MicroTari::from(1000000)); assert_eq!(balance.pending_incoming_balance, MicroTari::from(0)); @@ -1742,6 +1754,7 @@ async fn test_txo_validation() { mined_height: 1, mined_in_block: block1_header.hash(), output_hash: output1_tx_output.hash(), + mined_timestamp: 0, }, UtxoQueryResponse { output: Some(output2_tx_output.clone().into()), @@ -1749,6 +1762,7 @@ async fn test_txo_validation() { mined_height: 1, mined_in_block: block1_header.hash(), output_hash: output2_tx_output.hash(), + mined_timestamp: 0, }, UtxoQueryResponse { output: Some(output4_tx_output.clone().into()), @@ -1756,6 +1770,7 @@ async fn test_txo_validation() { mined_height: 5, mined_in_block: block5_header_reorg.hash(), output_hash: output4_tx_output.hash(), + mined_timestamp: 0, }, ]; @@ -1804,7 +1819,8 @@ async fn test_txo_validation() { .await .unwrap(); - // This is needed on a fast computer, otherwise the balance have not been updated correctly yet with the next step + // This is needed on a fast computer, otherwise the balance have not been updated correctly yet with the next + // step let mut event_stream = oms.output_manager_handle.get_event_stream(); let delay = sleep(Duration::from_secs(10)); tokio::pin!(delay); @@ -1963,6 +1979,7 @@ async fn test_txo_revalidation() { mined_height: 1, mined_in_block: block1_header.hash(), output_hash: output1_tx_output.hash(), + mined_timestamp: 0, }, UtxoQueryResponse { output: Some(output2_tx_output.clone().into()), @@ -1970,6 +1987,7 @@ async fn test_txo_revalidation() { mined_height: 1, mined_in_block: block1_header.hash(), output_hash: output2_tx_output.hash(), + mined_timestamp: 0, }, ]; diff --git a/base_layer/wallet/tests/output_manager_service_tests/storage.rs b/base_layer/wallet/tests/output_manager_service_tests/storage.rs index d74ed10cee..13a415c468 100644 --- a/base_layer/wallet/tests/output_manager_service_tests/storage.rs +++ b/base_layer/wallet/tests/output_manager_service_tests/storage.rs @@ -172,7 +172,7 @@ pub fn test_db_backend(backend: T) { // Set first pending tx to mined but unconfirmed let mut mmr_pos = 0; for o in &pending_txs[0].outputs_to_be_received { - db.set_received_output_mined_height(o.hash.clone(), 2, vec![], mmr_pos, false) + db.set_received_output_mined_height(o.hash.clone(), 2, vec![], mmr_pos, false, 0) .unwrap(); mmr_pos += 1; } @@ -192,7 +192,7 @@ pub fn test_db_backend(backend: T) { // Set second pending tx to mined and confirmed for o in &pending_txs[1].outputs_to_be_received { - db.set_received_output_mined_height(o.hash.clone(), 4, vec![], mmr_pos, true) + db.set_received_output_mined_height(o.hash.clone(), 4, vec![], mmr_pos, true, 0) .unwrap(); mmr_pos += 1; } @@ -397,7 +397,7 @@ pub async fn test_no_duplicate_outputs() { // add it to the database let result = db.add_unspent_output(uo.clone()); assert!(result.is_ok()); - let result = db.set_received_output_mined_height(uo.hash.clone(), 1, Vec::new(), 1, true); + let result = db.set_received_output_mined_height(uo.hash.clone(), 1, Vec::new(), 1, true, 0); assert!(result.is_ok()); let outputs = db.fetch_mined_unspent_outputs().unwrap(); assert_eq!(outputs.len(), 1); diff --git a/base_layer/wallet/tests/support/base_node_service_mock.rs b/base_layer/wallet/tests/support/base_node_service_mock.rs index e39d0fa205..a0ec7e8b54 100644 --- a/base_layer/wallet/tests/support/base_node_service_mock.rs +++ b/base_layer/wallet/tests/support/base_node_service_mock.rs @@ -79,7 +79,7 @@ impl MockBaseNodeService { pub fn set_base_node_state(&mut self, height: Option) { let (chain_metadata, is_synced) = match height { Some(height) => { - let metadata = ChainMetadata::new(height, Vec::new(), 0, 0, 0); + let metadata = ChainMetadata::new(height, Vec::new(), 0, 0, 0, 0); (Some(metadata), Some(true)) }, None => (None, None), @@ -94,7 +94,7 @@ impl MockBaseNodeService { } pub fn set_default_base_node_state(&mut self) { - let metadata = ChainMetadata::new(i64::MAX as u64, Vec::new(), 0, 0, 0); + let metadata = ChainMetadata::new(i64::MAX as u64, Vec::new(), 0, 0, 0, 0); self.state = BaseNodeState { chain_metadata: Some(metadata), is_synced: Some(true), diff --git a/base_layer/wallet/tests/support/comms_rpc.rs b/base_layer/wallet/tests/support/comms_rpc.rs index 07e7d3166c..9a130ff424 100644 --- a/base_layer/wallet/tests/support/comms_rpc.rs +++ b/base_layer/wallet/tests/support/comms_rpc.rs @@ -138,12 +138,14 @@ impl BaseNodeWalletRpcMockState { confirmations: 0, is_synced: true, height_of_longest_chain: 0, + mined_timestamp: None, })), transaction_query_batch_response: Arc::new(Mutex::new(TxQueryBatchResponsesProto { responses: vec![], tip_hash: Some(vec![]), is_synced: true, height_of_longest_chain: 0, + tip_mined_timestamp: Some(0), })), tip_info_response: Arc::new(Mutex::new(TipInfoResponse { metadata: Some(ChainMetadataProto { @@ -151,6 +153,7 @@ impl BaseNodeWalletRpcMockState { best_block: Some(Vec::new()), accumulated_difficulty: Vec::new(), pruned_height: 0, + timestamp: Some(0), }), is_synced: true, })), @@ -809,6 +812,7 @@ impl BaseNodeWalletService for BaseNodeWalletRpcMockService { outputs: b.utxos.clone().into_iter().map(|o| o.into()).collect(), height: b.height, header_hash: b.header_hash.clone(), + mined_timestamp: 0, }; tx.send(Ok(item)).await.unwrap(); } @@ -823,6 +827,7 @@ impl BaseNodeWalletService for BaseNodeWalletRpcMockService { outputs: b.utxos.clone().into_iter().map(|o| o.into()).collect(), height: b.height, header_hash: b.header_hash.clone(), + mined_timestamp: 0, }; tx.send(Ok(item)).await.unwrap(); } @@ -934,6 +939,7 @@ mod test { best_block: Some(Vec::new()), accumulated_difficulty: Vec::new(), pruned_height: 0, + timestamp: Some(0), }; service_state.set_tip_info_response(TipInfoResponse { metadata: Some(chain_metadata), diff --git a/base_layer/wallet/tests/transaction_service_tests/service.rs b/base_layer/wallet/tests/transaction_service_tests/service.rs index c946344cba..472a0079cd 100644 --- a/base_layer/wallet/tests/transaction_service_tests/service.rs +++ b/base_layer/wallet/tests/transaction_service_tests/service.rs @@ -182,7 +182,7 @@ async fn setup_transaction_service>( .await; let db = WalletDatabase::new(WalletSqliteDatabase::new(db_connection.clone(), None).unwrap()); - let metadata = ChainMetadata::new(std::i64::MAX as u64, Vec::new(), 0, 0, 0); + let metadata = ChainMetadata::new(std::i64::MAX as u64, Vec::new(), 0, 0, 0, 0); db.set_chain_metadata(metadata).await.unwrap(); @@ -1864,6 +1864,7 @@ async fn test_power_mode_updates() { confirmations: None, mined_height: None, mined_in_block: None, + mined_timestamp: None, }; let completed_tx2 = CompletedTransaction { @@ -1885,6 +1886,7 @@ async fn test_power_mode_updates() { confirmations: None, mined_height: None, mined_in_block: None, + mined_timestamp: None, }; tx_backend @@ -1918,6 +1920,7 @@ async fn test_power_mode_updates() { confirmations: 0, is_synced: true, height_of_longest_chain: 10, + mined_timestamp: None, }); let result = alice_ts_interface @@ -3322,6 +3325,7 @@ async fn test_coinbase_generation_and_monitoring() { block_hash: None, confirmations: 0, block_height: 0, + mined_timestamp: None, }, TxQueryBatchResponseProto { signature: Some(SignatureProto::from( @@ -3331,6 +3335,7 @@ async fn test_coinbase_generation_and_monitoring() { block_hash: Some(block_headers.get(&1).unwrap().hash()), confirmations: 0, block_height: 1, + mined_timestamp: Some(0), }, ]; let batch_query_response = TxQueryBatchResponsesProto { @@ -3338,6 +3343,7 @@ async fn test_coinbase_generation_and_monitoring() { is_synced: true, tip_hash: Some(block_headers.get(&1).unwrap().hash()), height_of_longest_chain: 1, + tip_mined_timestamp: Some(0), }; alice_ts_interface @@ -3384,6 +3390,7 @@ async fn test_coinbase_generation_and_monitoring() { block_hash: Some(block_headers.get(&4).unwrap().hash()), confirmations: 3, block_height: 4, + mined_timestamp: Some(0), }); let batch_query_response = TxQueryBatchResponsesProto { @@ -3391,6 +3398,7 @@ async fn test_coinbase_generation_and_monitoring() { is_synced: true, tip_hash: Some(block_headers.get(&4).unwrap().hash()), height_of_longest_chain: 4, + tip_mined_timestamp: Some(0), }; alice_ts_interface .base_node_rpc_mock_state @@ -3465,6 +3473,7 @@ async fn test_coinbase_abandoned() { block_hash: None, confirmations: 0, block_height: 0, + mined_timestamp: None, }]; let batch_query_response = TxQueryBatchResponsesProto { @@ -3472,6 +3481,7 @@ async fn test_coinbase_abandoned() { is_synced: true, tip_hash: Some([5u8; 16].to_vec()), height_of_longest_chain: block_height_a + TransactionServiceConfig::default().num_confirmations_required + 1, + tip_mined_timestamp: Some(0), }; alice_ts_interface @@ -3593,6 +3603,7 @@ async fn test_coinbase_abandoned() { block_hash: None, confirmations: 0, block_height: 0, + mined_timestamp: None, }, TxQueryBatchResponseProto { signature: Some(SignatureProto::from(tx2.first_kernel_excess_sig().unwrap().clone())), @@ -3600,6 +3611,7 @@ async fn test_coinbase_abandoned() { block_hash: Some([11u8; 16].to_vec()), confirmations: 2, block_height: block_height_b, + mined_timestamp: Some(0), }, ]; @@ -3608,6 +3620,7 @@ async fn test_coinbase_abandoned() { is_synced: true, tip_hash: Some([13u8; 16].to_vec()), height_of_longest_chain: block_height_b + 2, + tip_mined_timestamp: Some(0), }; alice_ts_interface @@ -3676,6 +3689,7 @@ async fn test_coinbase_abandoned() { block_hash: None, confirmations: 0, block_height: 0, + mined_timestamp: None, }, TxQueryBatchResponseProto { signature: Some(SignatureProto::from(tx2.first_kernel_excess_sig().unwrap().clone())), @@ -3683,6 +3697,7 @@ async fn test_coinbase_abandoned() { block_hash: None, confirmations: 0, block_height: 0, + mined_timestamp: None, }, ]; @@ -3691,6 +3706,7 @@ async fn test_coinbase_abandoned() { is_synced: true, tip_hash: Some([12u8; 16].to_vec()), height_of_longest_chain: block_height_b + TransactionServiceConfig::default().num_confirmations_required + 1, + tip_mined_timestamp: Some(0), }; alice_ts_interface @@ -3790,6 +3806,7 @@ async fn test_coinbase_abandoned() { block_hash: None, confirmations: 0, block_height: 0, + mined_timestamp: None, }, TxQueryBatchResponseProto { signature: Some(SignatureProto::from(tx2.first_kernel_excess_sig().unwrap().clone())), @@ -3797,6 +3814,7 @@ async fn test_coinbase_abandoned() { block_hash: Some(block_headers.get(&10).unwrap().hash()), confirmations: 5, block_height: 10, + mined_timestamp: Some(0), }, ]; @@ -3805,6 +3823,7 @@ async fn test_coinbase_abandoned() { is_synced: true, tip_hash: Some([20u8; 16].to_vec()), height_of_longest_chain: 20, + tip_mined_timestamp: Some(0), }; alice_ts_interface @@ -5039,6 +5058,7 @@ async fn transaction_service_tx_broadcast() { confirmations: TransactionServiceConfig::default().num_confirmations_required, is_synced: true, height_of_longest_chain: 0, + mined_timestamp: None, }); let delay = sleep(Duration::from_secs(60)); @@ -5107,6 +5127,7 @@ async fn transaction_service_tx_broadcast() { confirmations: TransactionServiceConfig::default().num_confirmations_required, is_synced: true, height_of_longest_chain: 0, + mined_timestamp: None, }); let alice_completed_tx2 = alice_ts_interface @@ -5197,6 +5218,7 @@ async fn broadcast_all_completed_transactions_on_startup() { confirmations: None, mined_height: None, mined_in_block: None, + mined_timestamp: None, }; let completed_tx2 = CompletedTransaction { @@ -5239,6 +5261,7 @@ async fn broadcast_all_completed_transactions_on_startup() { confirmations: TransactionServiceConfig::default().num_confirmations_required, is_synced: true, height_of_longest_chain: 0, + mined_timestamp: None, }); assert!(alice_ts_interface @@ -5310,6 +5333,7 @@ async fn test_update_faux_tx_on_oms_validation() { ImportStatus::Imported, None, None, + None, ) .await .unwrap(); @@ -5323,6 +5347,7 @@ async fn test_update_faux_tx_on_oms_validation() { ImportStatus::FauxUnconfirmed, None, None, + None, ) .await .unwrap(); @@ -5337,6 +5362,7 @@ async fn test_update_faux_tx_on_oms_validation() { ImportStatus::FauxConfirmed, None, None, + None, ) .await .unwrap(); diff --git a/base_layer/wallet/tests/transaction_service_tests/storage.rs b/base_layer/wallet/tests/transaction_service_tests/storage.rs index 6f16b5e33a..5454a6fe5a 100644 --- a/base_layer/wallet/tests/transaction_service_tests/storage.rs +++ b/base_layer/wallet/tests/transaction_service_tests/storage.rs @@ -24,7 +24,7 @@ use aes_gcm::{ aead::{generic_array::GenericArray, NewAead}, Aes256Gcm, }; -use chrono::Utc; +use chrono::{NaiveDateTime, Utc}; use rand::rngs::OsRng; use tari_common_types::{ transaction::{TransactionDirection, TransactionStatus, TxId}, @@ -280,6 +280,7 @@ pub fn test_db_backend(backend: T) { confirmations: None, mined_height: None, mined_in_block: None, + mined_timestamp: None, }); runtime .block_on(db.complete_outbound_transaction(outbound_txs[i].tx_id, completed_txs[i].clone())) @@ -327,7 +328,7 @@ pub fn test_db_backend(backend: T) { assert!(runtime.block_on(db.fetch_last_mined_transaction()).unwrap().is_none()); runtime - .block_on(db.set_transaction_mined_height(completed_txs[0].tx_id, 10, [0u8; 16].to_vec(), 5, true, false)) + .block_on(db.set_transaction_mined_height(completed_txs[0].tx_id, 10, [0u8; 16].to_vec(), 0, 5, true, false)) .unwrap(); assert_eq!( @@ -618,6 +619,7 @@ async fn import_tx_and_read_it_from_db() { TransactionDirection::Inbound, Some(0), Some(5), + Some(NaiveDateTime::from_timestamp(0, 0)), ); sqlite_db @@ -646,6 +648,7 @@ async fn import_tx_and_read_it_from_db() { TransactionDirection::Inbound, Some(0), Some(6), + Some(NaiveDateTime::from_timestamp(0, 0)), ); sqlite_db @@ -674,6 +677,7 @@ async fn import_tx_and_read_it_from_db() { TransactionDirection::Inbound, Some(0), Some(7), + Some(NaiveDateTime::from_timestamp(0, 0)), ); sqlite_db diff --git a/base_layer/wallet/tests/transaction_service_tests/transaction_protocols.rs b/base_layer/wallet/tests/transaction_service_tests/transaction_protocols.rs index 4668d57960..1eec1dca46 100644 --- a/base_layer/wallet/tests/transaction_service_tests/transaction_protocols.rs +++ b/base_layer/wallet/tests/transaction_service_tests/transaction_protocols.rs @@ -197,6 +197,7 @@ pub async fn add_transaction_to_database( TransactionDirection::Outbound, coinbase_block_height, None, + None, ); db.insert_completed_transaction(tx_id, completed_tx1).await.unwrap(); } @@ -400,6 +401,7 @@ async fn tx_broadcast_protocol_restart_protocol_as_query() { confirmations: 0, is_synced: true, height_of_longest_chain: 0, + mined_timestamp: None, }); let timeout_update_watch = Watch::new(Duration::from_secs(1)); @@ -429,6 +431,7 @@ async fn tx_broadcast_protocol_restart_protocol_as_query() { confirmations: 0, is_synced: true, height_of_longest_chain: 0, + mined_timestamp: None, }); // Should receive a resubmission call @@ -450,6 +453,7 @@ async fn tx_broadcast_protocol_restart_protocol_as_query() { confirmations: resources.config.num_confirmations_required, is_synced: true, height_of_longest_chain: 0, + mined_timestamp: None, }); // Check that the protocol ends with success @@ -506,6 +510,7 @@ async fn tx_broadcast_protocol_submit_success_followed_by_rejection() { confirmations: 0, is_synced: true, height_of_longest_chain: 0, + mined_timestamp: None, }); // Wait for 1 query @@ -608,6 +613,7 @@ async fn tx_broadcast_protocol_submit_already_mined() { confirmations: resources.config.num_confirmations_required, is_synced: true, height_of_longest_chain: 10, + mined_timestamp: None, }); // Check that the protocol ends with success @@ -645,6 +651,7 @@ async fn tx_broadcast_protocol_submit_and_base_node_gets_changed() { confirmations: 1, is_synced: true, height_of_longest_chain: 0, + mined_timestamp: None, }); let timeout_update_watch = Watch::new(Duration::from_secs(1)); @@ -688,6 +695,7 @@ async fn tx_broadcast_protocol_submit_and_base_node_gets_changed() { confirmations: resources.config.num_confirmations_required, is_synced: true, height_of_longest_chain: 0, + mined_timestamp: None, }); // Change Base Node @@ -763,6 +771,7 @@ async fn tx_validation_protocol_tx_becomes_mined_unconfirmed_then_confirmed() { block_hash: Some([1u8; 16].to_vec()), confirmations: 0, block_height: 1, + mined_timestamp: Some(0), }]; let mut batch_query_response = TxQueryBatchResponsesProto { @@ -770,6 +779,7 @@ async fn tx_validation_protocol_tx_becomes_mined_unconfirmed_then_confirmed() { is_synced: true, tip_hash: Some([1u8; 16].to_vec()), height_of_longest_chain: 1, + tip_mined_timestamp: Some(0), }; rpc_service_state.set_transaction_query_batch_responses(batch_query_response.clone()); @@ -835,6 +845,7 @@ async fn tx_validation_protocol_tx_becomes_mined_unconfirmed_then_confirmed() { block_hash: Some([5u8; 16].to_vec()), confirmations: 4, block_height: 5, + mined_timestamp: Some(0), }]; let batch_query_response = TxQueryBatchResponsesProto { @@ -842,6 +853,7 @@ async fn tx_validation_protocol_tx_becomes_mined_unconfirmed_then_confirmed() { is_synced: true, tip_hash: Some([5u8; 16].to_vec()), height_of_longest_chain: 5, + tip_mined_timestamp: Some(0), }; rpc_service_state.set_transaction_query_batch_responses(batch_query_response.clone()); @@ -916,6 +928,7 @@ async fn tx_revalidation() { block_hash: Some([5u8; 16].to_vec()), confirmations: 4, block_height: 5, + mined_timestamp: Some(0), }]; let batch_query_response = TxQueryBatchResponsesProto { @@ -923,6 +936,7 @@ async fn tx_revalidation() { is_synced: true, tip_hash: Some([5u8; 16].to_vec()), height_of_longest_chain: 5, + tip_mined_timestamp: Some(0), }; rpc_service_state.set_transaction_query_batch_responses(batch_query_response.clone()); @@ -956,6 +970,7 @@ async fn tx_revalidation() { block_hash: Some([5u8; 16].to_vec()), confirmations: 8, block_height: 10, + mined_timestamp: Some(0), }]; let batch_query_response = TxQueryBatchResponsesProto { @@ -963,6 +978,7 @@ async fn tx_revalidation() { is_synced: true, tip_hash: Some([5u8; 16].to_vec()), height_of_longest_chain: 10, + tip_mined_timestamp: Some(0), }; rpc_service_state.set_transaction_query_batch_responses(batch_query_response.clone()); @@ -1074,6 +1090,7 @@ async fn tx_validation_protocol_reorg() { block_hash: Some(block_headers.get(&5).unwrap().hash()), confirmations: 5, block_height: 5, + mined_timestamp: Some(0), }, TxQueryBatchResponseProto { signature: Some(SignatureProto::from( @@ -1083,6 +1100,7 @@ async fn tx_validation_protocol_reorg() { block_hash: Some(block_headers.get(&6).unwrap().hash()), confirmations: 4, block_height: 6, + mined_timestamp: Some(0), }, TxQueryBatchResponseProto { signature: Some(SignatureProto::from( @@ -1092,6 +1110,7 @@ async fn tx_validation_protocol_reorg() { block_hash: Some(block_headers.get(&7).unwrap().hash()), confirmations: 3, block_height: 7, + mined_timestamp: Some(0), }, TxQueryBatchResponseProto { signature: Some(SignatureProto::from( @@ -1101,6 +1120,7 @@ async fn tx_validation_protocol_reorg() { block_hash: Some(block_headers.get(&8).unwrap().hash()), confirmations: 2, block_height: 8, + mined_timestamp: Some(0), }, TxQueryBatchResponseProto { signature: Some(SignatureProto::from( @@ -1110,6 +1130,7 @@ async fn tx_validation_protocol_reorg() { block_hash: Some(block_headers.get(&8).unwrap().hash()), confirmations: 2, block_height: 8, + mined_timestamp: Some(0), }, TxQueryBatchResponseProto { signature: Some(SignatureProto::from( @@ -1119,6 +1140,7 @@ async fn tx_validation_protocol_reorg() { block_hash: Some(block_headers.get(&9).unwrap().hash()), confirmations: 1, block_height: 9, + mined_timestamp: Some(0), }, TxQueryBatchResponseProto { signature: Some(SignatureProto::from( @@ -1128,6 +1150,7 @@ async fn tx_validation_protocol_reorg() { block_hash: Some(block_headers.get(&9).unwrap().hash()), confirmations: 1, block_height: 9, + mined_timestamp: Some(0), }, ]; @@ -1136,6 +1159,7 @@ async fn tx_validation_protocol_reorg() { is_synced: true, tip_hash: Some(block_headers.get(&10).unwrap().hash()), height_of_longest_chain: 10, + tip_mined_timestamp: Some(0), }; rpc_service_state.set_transaction_query_batch_responses(batch_query_response.clone()); @@ -1187,6 +1211,7 @@ async fn tx_validation_protocol_reorg() { block_hash: Some(block_headers.get(&5).unwrap().hash()), confirmations: 4, block_height: 5, + mined_timestamp: Some(0), }, TxQueryBatchResponseProto { signature: Some(SignatureProto::from( @@ -1196,6 +1221,7 @@ async fn tx_validation_protocol_reorg() { block_hash: Some(block_headers.get(&6).unwrap().hash()), confirmations: 3, block_height: 6, + mined_timestamp: Some(0), }, TxQueryBatchResponseProto { signature: Some(SignatureProto::from( @@ -1205,6 +1231,7 @@ async fn tx_validation_protocol_reorg() { block_hash: Some(block_headers.get(&7).unwrap().hash()), confirmations: 2, block_height: 7, + mined_timestamp: Some(0), }, TxQueryBatchResponseProto { signature: Some(SignatureProto::from( @@ -1214,6 +1241,7 @@ async fn tx_validation_protocol_reorg() { block_hash: None, confirmations: 0, block_height: 0, + mined_timestamp: None, }, TxQueryBatchResponseProto { signature: Some(SignatureProto::from( @@ -1223,6 +1251,7 @@ async fn tx_validation_protocol_reorg() { block_hash: Some(block_headers.get(&8).unwrap().hash()), confirmations: 1, block_height: 8, + mined_timestamp: Some(0), }, TxQueryBatchResponseProto { signature: Some(SignatureProto::from( @@ -1232,6 +1261,7 @@ async fn tx_validation_protocol_reorg() { block_hash: None, confirmations: 0, block_height: 0, + mined_timestamp: None, }, ]; @@ -1240,6 +1270,7 @@ async fn tx_validation_protocol_reorg() { is_synced: true, tip_hash: Some(block_headers.get(&8).unwrap().hash()), height_of_longest_chain: 8, + tip_mined_timestamp: Some(0), }; rpc_service_state.set_transaction_query_batch_responses(batch_query_response.clone()); diff --git a/base_layer/wallet/tests/utxo_scanner.rs b/base_layer/wallet/tests/utxo_scanner.rs index d8c9a4f720..72b1f3bfa9 100644 --- a/base_layer/wallet/tests/utxo_scanner.rs +++ b/base_layer/wallet/tests/utxo_scanner.rs @@ -309,6 +309,7 @@ async fn test_utxo_scanner_recovery() { best_block: Some(block_headers.get(&(NUM_BLOCKS - 1)).unwrap().clone().hash()), accumulated_difficulty: Vec::new(), pruned_height: 0, + timestamp: Some(0), }; test_interface.rpc_service_state.set_tip_info_response(TipInfoResponse { metadata: Some(chain_metadata), @@ -398,6 +399,7 @@ async fn test_utxo_scanner_recovery_with_restart() { best_block: Some(block_headers.get(&(NUM_BLOCKS - 1)).unwrap().clone().hash()), accumulated_difficulty: Vec::new(), pruned_height: 0, + timestamp: Some(0), }; test_interface.rpc_service_state.set_tip_info_response(TipInfoResponse { metadata: Some(chain_metadata.clone()), @@ -448,6 +450,7 @@ async fn test_utxo_scanner_recovery_with_restart() { import_status: _, tx_id: _, current_height: _, + mined_timestamp: _, } = req { assert_eq!(message, "Output found on blockchain during Wallet Recovery".to_string()); @@ -514,6 +517,7 @@ async fn test_utxo_scanner_recovery_with_restart() { import_status: _, tx_id: _, current_height: _, + mined_timestamp: _, } = req { assert_eq!(message, "recovery".to_string()); @@ -551,6 +555,7 @@ async fn test_utxo_scanner_recovery_with_restart_and_reorg() { best_block: Some(block_headers.get(&(NUM_BLOCKS - 1)).unwrap().clone().hash()), accumulated_difficulty: Vec::new(), pruned_height: 0, + timestamp: Some(0), }; test_interface.rpc_service_state.set_tip_info_response(TipInfoResponse { metadata: Some(chain_metadata.clone()), @@ -613,6 +618,7 @@ async fn test_utxo_scanner_recovery_with_restart_and_reorg() { best_block: Some(block_headers.get(&9).unwrap().clone().hash()), accumulated_difficulty: Vec::new(), pruned_height: 0, + timestamp: Some(0), }; test_interface2 .rpc_service_state @@ -715,6 +721,7 @@ async fn test_utxo_scanner_scanned_block_cache_clearing() { best_block: Some(block_headers.get(&(800 + NUM_BLOCKS - 1)).unwrap().clone().hash()), accumulated_difficulty: Vec::new(), pruned_height: 0, + timestamp: Some(0), }; test_interface.rpc_service_state.set_tip_info_response(TipInfoResponse { metadata: Some(chain_metadata), @@ -810,6 +817,7 @@ async fn test_utxo_scanner_one_sided_payments() { best_block: Some(block_headers.get(&(NUM_BLOCKS - 1)).unwrap().clone().hash()), accumulated_difficulty: Vec::new(), pruned_height: 0, + timestamp: Some(0), }; test_interface.rpc_service_state.set_tip_info_response(TipInfoResponse { metadata: Some(chain_metadata), @@ -872,6 +880,7 @@ async fn test_utxo_scanner_one_sided_payments() { import_status: _, tx_id: _, current_height: _, + mined_timestamp: _, } = req { assert_eq!(message, "one-sided non-default".to_string()); @@ -911,6 +920,7 @@ async fn test_utxo_scanner_one_sided_payments() { best_block: Some(block_headers.get(&(NUM_BLOCKS)).unwrap().clone().hash()), accumulated_difficulty: Vec::new(), pruned_height: 0, + timestamp: Some(0), }; test_interface.rpc_service_state.set_tip_info_response(TipInfoResponse { @@ -957,6 +967,7 @@ async fn test_utxo_scanner_one_sided_payments() { import_status: _, tx_id: _, current_height: h, + mined_timestamp: _, } = req { println!("{:?}", h); diff --git a/base_layer/wallet/tests/wallet.rs b/base_layer/wallet/tests/wallet.rs index 05af980cd9..d59fb1b567 100644 --- a/base_layer/wallet/tests/wallet.rs +++ b/base_layer/wallet/tests/wallet.rs @@ -169,7 +169,7 @@ async fn create_wallet( ..Default::default() }; - let metadata = ChainMetadata::new(i64::MAX as u64, Vec::new(), 0, 0, 0); + let metadata = ChainMetadata::new(i64::MAX as u64, Vec::new(), 0, 0, 0, 0); let _db_value = wallet_backend.write(WriteOperation::Insert(DbKeyValuePair::BaseNodeChainMetadata(metadata))); diff --git a/base_layer/wallet_ffi/Cargo.toml b/base_layer/wallet_ffi/Cargo.toml index 6a892bb0ea..d6d9171845 100644 --- a/base_layer/wallet_ffi/Cargo.toml +++ b/base_layer/wallet_ffi/Cargo.toml @@ -31,6 +31,7 @@ thiserror = "1.0.26" tokio = "1.11" env_logger = "0.7.0" num-traits = "0.2.15" +itertools = "0.10.3" # # Temporary workaround until crates utilizing openssl have been updated from security-framework 2.4.0 diff --git a/base_layer/wallet_ffi/build.rs b/base_layer/wallet_ffi/build.rs index 8db77610db..8c41d640db 100644 --- a/base_layer/wallet_ffi/build.rs +++ b/base_layer/wallet_ffi/build.rs @@ -3,16 +3,13 @@ use std::{env, path::PathBuf}; -use cbindgen::{Config, Language, ParseConfig, Style}; +use cbindgen::{Config, ExportConfig, Language, ParseConfig, Style}; fn main() { let crate_dir = env::var("CARGO_MANIFEST_DIR").unwrap(); - let package_name = env::var("CARGO_PKG_NAME").unwrap(); - let output_file = PathBuf::from(&crate_dir) - .join(format!("{}.h", package_name)) - .display() - .to_string(); + // let package_name = env::var("CARGO_PKG_NAME").unwrap(); + let output_file = PathBuf::from(&crate_dir).join("wallet.h").display().to_string(); let config = Config { language: Language::C, @@ -28,7 +25,13 @@ fn main() { ]), ..Default::default() }, + autogen_warning: Some("// This file was generated by cargo-bindgen. Please do not edit manually.".to_string()), style: Style::Tag, + cpp_compat: true, + export: ExportConfig { + include: vec!["TariUtxo".to_string()], + ..Default::default() + }, ..Default::default() }; diff --git a/base_layer/wallet_ffi/mobile_build.sh b/base_layer/wallet_ffi/mobile_build.sh index 4ecb3630b6..1db94d71ab 100755 --- a/base_layer/wallet_ffi/mobile_build.sh +++ b/base_layer/wallet_ffi/mobile_build.sh @@ -75,7 +75,7 @@ if [ -n "${DEPENDENCIES}" ] && [ -n "${PKG_PATH}" ] && [ "${BUILD_IOS}" -eq 1 ] if [ "${CARGO_CLEAN}" -eq "1" ]; then cargo clean >> "${IOS_LOG_PATH}/cargo.txt" 2>&1 fi - cp tari_wallet_ffi.h "${DEPENDENCIES}/MobileWallet/TariLib/" + cp wallet.h "${DEPENDENCIES}/MobileWallet/TariLib/" export PKG_CONFIG_PATH=${PKG_PATH} # shellcheck disable=SC2028 echo "\t${CYAN}Building Wallet FFI${NC}" @@ -424,7 +424,7 @@ EOF else cargo ndk --target ${PLATFORMABI} --android-platform ${LEVEL} -- build --release > "${ANDROID_LOG_PATH}/cargo_${PLATFORMABI}_${LEVEL}.txt" 2>&1 fi - cp tari_wallet_ffi.h "${DEPENDENCIES}/" + cp wallet.h "${DEPENDENCIES}/" rm -rf .cargo cd ../.. cd target || exit diff --git a/base_layer/wallet_ffi/src/callback_handler_tests.rs b/base_layer/wallet_ffi/src/callback_handler_tests.rs index ca83e8b49d..7e14b2bf1a 100644 --- a/base_layer/wallet_ffi/src/callback_handler_tests.rs +++ b/base_layer/wallet_ffi/src/callback_handler_tests.rs @@ -9,7 +9,7 @@ mod test { time::Duration, }; - use chrono::Utc; + use chrono::{NaiveDateTime, Utc}; use rand::rngs::OsRng; use tari_common_types::{ transaction::{TransactionDirection, TransactionStatus}, @@ -270,6 +270,7 @@ mod test { TransactionDirection::Inbound, None, None, + None, ); runtime .block_on(db.insert_completed_transaction(2u64.into(), completed_tx.clone())) @@ -331,6 +332,7 @@ mod test { TransactionDirection::Inbound, None, Some(2), + Some(NaiveDateTime::from_timestamp(0, 0)), ); runtime .block_on(db.insert_completed_transaction(6u64.into(), faux_unconfirmed_tx.clone())) @@ -355,6 +357,7 @@ mod test { TransactionDirection::Inbound, None, Some(5), + Some(NaiveDateTime::from_timestamp(0, 0)), ); runtime .block_on(db.insert_completed_transaction(7u64.into(), faux_confirmed_tx.clone())) diff --git a/base_layer/wallet_ffi/src/lib.rs b/base_layer/wallet_ffi/src/lib.rs index 72d182715a..d37d6e27de 100644 --- a/base_layer/wallet_ffi/src/lib.rs +++ b/base_layer/wallet_ffi/src/lib.rs @@ -56,6 +56,7 @@ use std::{ boxed::Box, convert::{TryFrom, TryInto}, ffi::{CStr, CString}, + fmt::{Display, Formatter}, mem::ManuallyDrop, num::NonZeroU16, path::PathBuf, @@ -67,7 +68,8 @@ use std::{ use chrono::{DateTime, Local}; use error::LibWalletError; -use libc::{c_char, c_int, c_uchar, c_uint, c_ulonglong, c_ushort}; +use itertools::Itertools; +use libc::{c_char, c_int, c_uchar, c_uint, c_ulonglong, c_ushort, c_void}; use log::{LevelFilter, *}; use log4rs::{ append::{ @@ -138,6 +140,7 @@ use tari_wallet::{ error::OutputManagerError, storage::{ database::{OutputBackendQuery, OutputManagerDatabase, SortDirection}, + models::DbUnblindedOutput, OutputStatus, }, }, @@ -229,28 +232,353 @@ pub struct TariWallet { shutdown: Shutdown, } +#[derive(Debug)] +#[repr(C)] +pub struct TariCoinPreview { + pub expected_outputs: *mut TariVector, + pub fee: u64, +} + +#[derive(Debug)] +#[repr(C)] +pub enum TariUtxoSort { + ValueAsc = 0, + ValueDesc = 1, + MinedHeightAsc = 2, + MinedHeightDesc = 3, +} + +#[derive(Debug, Copy, Clone, Eq, PartialEq)] +#[repr(C)] +pub enum TariTypeTag { + Text = 0, + Utxo = 1, + Commitment = 2, + U64 = 3, + I64 = 4, +} + +impl Display for TariTypeTag { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + match self { + TariTypeTag::Text => write!(f, "Text"), + TariTypeTag::Utxo => write!(f, "Utxo"), + TariTypeTag::Commitment => write!(f, "Commitment"), + TariTypeTag::U64 => write!(f, "U64"), + TariTypeTag::I64 => write!(f, "I64"), + } + } +} + #[derive(Debug, Clone)] #[repr(C)] pub struct TariUtxo { - pub commitment: *mut c_char, + pub commitment: *const c_char, pub value: u64, + pub mined_height: u64, + pub mined_timestamp: u64, + pub status: u8, +} + +impl From for TariUtxo { + fn from(x: DbUnblindedOutput) -> Self { + Self { + commitment: CString::new(x.commitment.to_hex()) + .expect("failed to obtain hex from a commitment") + .into_raw(), + value: x.unblinded_output.value.as_u64(), + mined_height: x.mined_height.unwrap_or(0), + mined_timestamp: x + .mined_timestamp + .map(|ts| ts.timestamp_millis() as u64) + .unwrap_or_default(), + status: match x.status { + OutputStatus::Unspent => 0, + OutputStatus::Spent => 1, + OutputStatus::EncumberedToBeReceived => 2, + OutputStatus::EncumberedToBeSpent => 3, + OutputStatus::Invalid => 4, + OutputStatus::CancelledInbound => 5, + OutputStatus::UnspentMinedUnconfirmed => 6, + OutputStatus::ShortTermEncumberedToBeReceived => 7, + OutputStatus::ShortTermEncumberedToBeSpent => 8, + OutputStatus::SpentMinedUnconfirmed => 9, + OutputStatus::AbandonedCoinbase => 10, + OutputStatus::NotStored => 11, + }, + } + } } +/// -------------------------------- Vector ------------------------------------------------ /// + #[derive(Debug, Clone)] #[repr(C)] -pub struct TariOutputs { +pub struct TariVector { + pub tag: TariTypeTag, pub len: usize, pub cap: usize, - pub ptr: *mut TariUtxo, + pub ptr: *mut c_void, } -#[derive(Debug)] -#[repr(C)] -pub enum TariUtxoSort { - ValueAsc, - ValueDesc, - MinedHeightAsc, - MinedHeightDesc, +impl From> for TariVector { + fn from(v: Vec) -> Self { + let mut v = ManuallyDrop::new(v); + + Self { + tag: TariTypeTag::I64, + len: v.len(), + cap: v.capacity(), + ptr: v.as_mut_ptr() as *mut c_void, + } + } +} + +impl From> for TariVector { + fn from(v: Vec) -> Self { + let mut v = ManuallyDrop::new(v); + + Self { + tag: TariTypeTag::U64, + len: v.len(), + cap: v.capacity(), + ptr: v.as_mut_ptr() as *mut c_void, + } + } +} + +impl From> for TariVector { + fn from(v: Vec) -> Self { + let mut v = ManuallyDrop::new( + v.into_iter() + .map(|x| CString::new(x.as_str()).unwrap().into_raw()) + .collect::>(), + ); + + Self { + tag: TariTypeTag::Text, + len: v.len(), + cap: v.capacity(), + ptr: v.as_mut_ptr() as *mut c_void, + } + } +} + +impl From> for TariVector { + fn from(v: Vec) -> Self { + let mut v = ManuallyDrop::new( + v.into_iter() + .map(|x| CString::new(x.to_hex().as_str()).unwrap().into_raw()) + .collect::>(), + ); + + Self { + tag: TariTypeTag::Commitment, + len: v.len(), + cap: v.capacity(), + ptr: v.as_mut_ptr() as *mut c_void, + } + } +} + +impl From> for TariVector { + fn from(v: Vec) -> TariVector { + let mut v = ManuallyDrop::new(v.into_iter().map(TariUtxo::from).collect_vec()); + + Self { + tag: TariTypeTag::Utxo, + len: v.len(), + cap: v.capacity(), + ptr: v.as_mut_ptr() as *mut c_void, + } + } +} + +impl From> for TariVector { + fn from(v: Vec) -> TariVector { + let mut v = ManuallyDrop::new(v.into_iter().map(|x| x as i32 as u64).collect_vec()); + + Self { + tag: TariTypeTag::U64, + len: v.len(), + cap: v.capacity(), + ptr: v.as_mut_ptr() as *mut c_void, + } + } +} + +#[allow(dead_code)] +impl TariVector { + fn to_string_vec(&self) -> Result, InterfaceError> { + if self.tag != TariTypeTag::Text { + return Err(InterfaceError::InvalidArgument(format!( + "expecting String, got {}", + self.tag + ))); + } + + if self.ptr.is_null() { + return Err(InterfaceError::NullError(String::from( + "tari vector of strings has null pointer", + ))); + } + + Ok(unsafe { + Vec::from_raw_parts(self.ptr as *mut *mut c_char, self.len, self.cap) + .into_iter() + .map(|x| { + CStr::from_ptr(x) + .to_str() + .expect("failed to convert from a vector of strings") + .to_string() + }) + .collect() + }) + } + + fn to_commitment_vec(&self) -> Result, InterfaceError> { + self.to_string_vec()? + .into_iter() + .map(|x| { + Commitment::from_hex(x.as_str()) + .map_err(|e| InterfaceError::PointerError(format!("failed to convert hex to commitment: {:?}", e))) + }) + .try_collect::, InterfaceError>() + } + + #[allow(dead_code)] + fn to_utxo_vec(&self) -> Result, InterfaceError> { + if self.tag != TariTypeTag::Utxo { + return Err(InterfaceError::InvalidArgument(format!( + "expecting Utxo, got {}", + self.tag + ))); + } + + if self.ptr.is_null() { + return Err(InterfaceError::NullError(String::from( + "tari vector of utxos has null pointer", + ))); + } + + Ok(unsafe { Vec::from_raw_parts(self.ptr as *mut TariUtxo, self.len, self.cap) }) + } +} + +/// Initialize a new `TariVector` +/// +/// ## Arguments +/// `tag` - A predefined type-tag of the vector's payload. +/// +/// ## Returns +/// `*mut TariVector` - Returns a pointer to a `TariVector`. +/// +/// # Safety +/// `destroy_tari_vector()` must be called to free the allocated memory. +#[no_mangle] +pub unsafe extern "C" fn create_tari_vector(tag: TariTypeTag) -> *mut TariVector { + let mut v = ManuallyDrop::new(Vec::with_capacity(2)); + Box::into_raw(Box::new(TariVector { + tag, + len: v.len(), + cap: v.capacity(), + ptr: v.as_mut_ptr() as *mut c_void, + })) +} + +/// Appending a given value to the back of the vector. +/// +/// ## Arguments +/// `s` - An item to push. +/// +/// ## Returns +/// +/// +/// # Safety +/// `destroy_tari_vector()` must be called to free the allocated memory. +#[no_mangle] +pub unsafe extern "C" fn tari_vector_push_string(tv: *mut TariVector, s: *const c_char, error_ptr: *mut i32) { + if tv.is_null() { + error!(target: LOG_TARGET, "tari vector pointer is null"); + ptr::replace( + error_ptr, + LibWalletError::from(InterfaceError::NullError("vector".to_string())).code, + ); + return; + } + + // unpacking into native vector + let mut v = match (*tv).to_string_vec() { + Ok(v) => v, + Err(e) => { + error!(target: LOG_TARGET, "{:#?}", e); + ptr::replace(error_ptr, LibWalletError::from(e).code); + return; + }, + }; + + let s = match CStr::from_ptr(s).to_str() { + Ok(cs) => cs.to_string(), + Err(e) => { + error!(target: LOG_TARGET, "failed to convert `s` into native string {:#?}", e); + ptr::replace( + error_ptr, + LibWalletError::from(InterfaceError::PointerError("invalid string".to_string())).code, + ); + return; + }, + }; + + // appending new value + // NOTE: relying on native vector's re-allocation + v.push(s); + + let mut v = ManuallyDrop::new( + v.into_iter() + .map(|x| CString::new(x.as_str()).unwrap().into_raw()) + .collect::>(), + ); + + (*tv).len = v.len(); + (*tv).cap = v.capacity(); + (*tv).ptr = v.as_mut_ptr() as *mut c_void; + ptr::replace(error_ptr, 0); +} + +/// Frees memory allocated for `TariVector`. +/// +/// ## Arguments +/// `v` - The pointer to `TariVector` +/// +/// ## Returns +/// `()` - Does not return a value, equivalent to void in C +/// +/// # Safety +/// None +#[no_mangle] +pub unsafe extern "C" fn destroy_tari_vector(v: *mut TariVector) { + if !v.is_null() { + let x = Box::from_raw(v); + let _ = x.ptr; + } +} + +/// Frees memory allocated for `TariCoinPreview`. +/// +/// ## Arguments +/// `v` - The pointer to `TariCoinPreview` +/// +/// ## Returns +/// `()` - Does not return a value, equivalent to void in C +/// +/// # Safety +/// None +#[no_mangle] +pub unsafe extern "C" fn destroy_tari_coin_preview(p: *mut TariCoinPreview) { + if !p.is_null() { + let x = Box::from_raw(p); + destroy_tari_vector(x.expected_outputs); + } } /// -------------------------------- Strings ------------------------------------------------ /// @@ -3940,7 +4268,7 @@ pub unsafe extern "C" fn wallet_create( .to_str() .expect("A non-null network should be able to be converted to string"); error!(target: LOG_TARGET, "network set to {}", network); - eprintln!("network set to {}", network); + // eprintln!("network set to {}", network); match Network::from_str(&*network) { Ok(n) => n, Err(_) => { @@ -4193,21 +4521,21 @@ pub unsafe extern "C" fn wallet_get_balance(wallet: *mut TariWallet, error_out: /// This function returns a list of unspent UTXO values and commitments. /// /// ## Arguments -/// `wallet` - The TariWallet pointer, -/// `page` - Page offset, -/// `page_size` - A number of items per page, -/// `sorting` - An enum representing desired sorting, -/// `dust_threshold` - A value filtering threshold. Outputs whose values are <= `dust_threshold` are not listed in the +/// * `wallet` - The TariWallet pointer, +/// * `page` - Page offset, +/// * `page_size` - A number of items per page, +/// * `sorting` - An enum representing desired sorting, +/// * `dust_threshold` - A value filtering threshold. Outputs whose values are <= `dust_threshold` are not listed in the /// result. -/// `error_out` - A pointer to an int which will be modified to an error -/// code should one occur, may not be null. Functions as an out parameter. +/// * `error_out` - Pointer to an int which will be modified to an error code should one occur, may not be null. +/// Functions as an out parameter. /// /// ## Returns -/// `*mut TariOutputs` - Returns a struct with an array pointer, length and capacity (needed for proper destruction +/// `*mut TariVector` - Returns a struct with an array pointer, length and capacity (needed for proper destruction /// after use). /// /// # Safety -/// `destroy_tari_outputs()` must be called after use. +/// `destroy_tari_vector()` must be called after use. /// Items that fail to produce `.as_transaction_output()` are omitted from the list and a `warn!()` message is logged to /// LOG_TARGET. #[no_mangle] @@ -4216,26 +4544,39 @@ pub unsafe extern "C" fn wallet_get_utxos( page: usize, page_size: usize, sorting: TariUtxoSort, + states: *mut TariVector, dust_threshold: u64, - error_out: *mut i32, -) -> *mut TariOutputs { + error_ptr: *mut i32, +) -> *mut TariVector { if wallet.is_null() { error!(target: LOG_TARGET, "wallet pointer is null"); ptr::replace( - error_out, - LibWalletError::from(InterfaceError::NullError("wallet".to_string())).code as c_int, + error_ptr, + LibWalletError::from(InterfaceError::NullError("wallet".to_string())).code, ); return ptr::null_mut(); } - let page = i64::from_usize(page).unwrap_or(i64::MAX).max(1) - 1; - let page_size = i64::from_usize(page_size).unwrap_or(i64::MAX).max(1); + let page = i64::from_usize(page).unwrap_or(i64::MAX); + let page_size = i64::from_usize(page_size).unwrap_or(i64::MAX); let dust_threshold = i64::from_u64(dust_threshold).unwrap_or(0); + let status = { + if states.is_null() { + vec![] + } else { + Vec::from_raw_parts((*states).ptr as *mut u64, (*states).len, (*states).cap) + .into_iter() + .map(|x| OutputStatus::try_from(x as i32).unwrap()) + .collect_vec() + } + }; + use SortDirection::{Asc, Desc}; let q = OutputBackendQuery { tip_height: i64::MAX, - status: vec![OutputStatus::Unspent], + status, + commitments: vec![], pagination: Some((page, page_size)), value_min: Some((dust_threshold, false)), value_max: None, @@ -4248,146 +4589,468 @@ pub unsafe extern "C" fn wallet_get_utxos( }; match (*wallet).wallet.output_db.fetch_outputs_by(q) { - Ok(unblinded_outputs) => { - let outputs: Vec = unblinded_outputs - .into_iter() - .filter_map(|out| { - Some(TariUtxo { - value: out.unblinded_output.value.as_u64(), - commitment: match out.unblinded_output.as_transaction_output(&CryptoFactories::default()) { - Ok(tout) => match CString::new(tout.commitment.to_hex()) { - Ok(cstr) => cstr.into_raw(), - Err(e) => { - error!( - target: LOG_TARGET, - "failed to convert commitment hex String into CString: {:#?}", e - ); - return None; - }, - }, - Err(e) => { - error!( - target: LOG_TARGET, - "failed to obtain commitment from the transaction output: {:#?}", e - ); - return None; - }, - }, - }) - }) - .collect(); - - let mut outputs = ManuallyDrop::new(outputs); - Box::into_raw(Box::new(TariOutputs { - len: outputs.len(), - cap: outputs.capacity(), - ptr: outputs.as_mut_ptr(), - })) + Ok(outputs) => { + ptr::replace(error_ptr, 0); + Box::into_raw(Box::new(TariVector::from(outputs))) }, Err(e) => { error!(target: LOG_TARGET, "failed to obtain outputs: {:#?}", e); ptr::replace( - error_out, + error_ptr, LibWalletError::from(WalletError::OutputManagerError( OutputManagerError::OutputManagerStorageError(e), )) - .code as c_int, + .code, ); ptr::null_mut() }, } } -/// Frees memory for a `TariOutputs` +/// This function returns a list of all UTXO values, commitment's hex values and states. /// /// ## Arguments -/// `x` - The pointer to `TariOutputs` +/// * `wallet` - The TariWallet pointer, +/// * `error_out` - Pointer to an int which will be modified to an error code should one occur, may not be null. +/// Functions as an out parameter. /// /// ## Returns -/// `()` - Does not return a value, equivalent to void in C +/// `*mut TariVector` - Returns a struct with an array pointer, length and capacity (needed for proper destruction +/// after use). /// -/// # Safety -/// None +/// ## States +/// 0 - Unspent +/// 1 - Spent +/// 2 - EncumberedToBeReceived +/// 3 - EncumberedToBeSpent +/// 4 - Invalid +/// 5 - CancelledInbound +/// 6 - UnspentMinedUnconfirmed +/// 7 - ShortTermEncumberedToBeReceived +/// 8 - ShortTermEncumberedToBeSpent +/// 9 - SpentMinedUnconfirmed +/// 10 - AbandonedCoinbase +/// 11 - NotStored +/// +/// # Safety +/// `destroy_tari_vector()` must be called after use. +/// Items that fail to produce `.as_transaction_output()` are omitted from the list and a `warn!()` message is logged to +/// LOG_TARGET. #[no_mangle] -pub unsafe extern "C" fn destroy_tari_outputs(x: *mut TariOutputs) { - if !x.is_null() { - Vec::from_raw_parts((*x).ptr, (*x).len, (*x).cap); - Box::from_raw(x); +pub unsafe extern "C" fn wallet_get_all_utxos(wallet: *mut TariWallet, error_ptr: *mut i32) -> *mut TariVector { + if wallet.is_null() { + error!(target: LOG_TARGET, "wallet pointer is null"); + ptr::replace( + error_ptr, + LibWalletError::from(InterfaceError::NullError("wallet".to_string())).code, + ); + return ptr::null_mut(); + } + + let q = OutputBackendQuery { + tip_height: i64::MAX, + status: vec![], + commitments: vec![], + pagination: None, + value_min: None, + value_max: None, + sorting: vec![], + }; + + match (*wallet).wallet.output_db.fetch_outputs_by(q) { + Ok(outputs) => { + ptr::replace(error_ptr, 0); + Box::into_raw(Box::new(TariVector::from(outputs))) + }, + + Err(e) => { + error!(target: LOG_TARGET, "failed to obtain outputs: {:#?}", e); + ptr::replace( + error_ptr, + LibWalletError::from(WalletError::OutputManagerError( + OutputManagerError::OutputManagerStorageError(e), + )) + .code, + ); + ptr::null_mut() + }, } } -/// Signs a message using the public key of the TariWallet +/// This function will tell the wallet to do a coin split. /// /// ## Arguments -/// `wallet` - The TariWallet pointer. -/// `msg` - The message pointer. -/// `error_out` - Pointer to an int which will be modified to an error code should one occur, may not be null. Functions -/// as an out parameter. +/// * `wallet` - The TariWallet pointer +/// * `commitments` - A `TariVector` of "strings", tagged as `TariTypeTag::String`, containing commitment's hex values +/// (see `Commitment::to_hex()`) +/// * `number_of_splits` - The number of times to split the amount +/// * `fee_per_gram` - The transaction fee +/// * `error_out` - Pointer to an int which will be modified to an error code should one occur, may not be null. +/// Functions as an out parameter. +/// /// ## Returns -/// `*mut c_char` - Returns the pointer to the hexadecimal representation of the signature and -/// public nonce, seperated by a pipe character. Empty if an error occured. +/// `c_ulonglong` - Returns the transaction id. /// /// # Safety -/// The ```string_destroy``` method must be called when finished with a string coming from rust to prevent a memory leak +/// `TariVector` must be freed after use with `destroy_tari_vector()` #[no_mangle] -pub unsafe extern "C" fn wallet_sign_message( +pub unsafe extern "C" fn wallet_coin_split( wallet: *mut TariWallet, - msg: *const c_char, - error_out: *mut c_int, -) -> *mut c_char { - let mut error = 0; - let mut result = CString::new("").expect("Blank CString will not fail."); - - ptr::swap(error_out, &mut error as *mut c_int); + commitments: *mut TariVector, + number_of_splits: usize, + fee_per_gram: u64, + error_ptr: *mut i32, +) -> u64 { if wallet.is_null() { - error = LibWalletError::from(InterfaceError::NullError("wallet".to_string())).code; - ptr::swap(error_out, &mut error as *mut c_int); - return result.into_raw(); - } - - if msg.is_null() { - error = LibWalletError::from(InterfaceError::NullError("message".to_string())).code; - ptr::swap(error_out, &mut error as *mut c_int); - return result.into_raw(); + error!(target: LOG_TARGET, "wallet pointer is null"); + ptr::replace( + error_ptr, + LibWalletError::from(InterfaceError::NullError("wallet".to_string())).code as c_int, + ); + return 0; } - let nonce = TariPrivateKey::random(&mut OsRng); - let secret = (*wallet).wallet.comms.node_identity().secret_key().clone(); - let message = CStr::from_ptr(msg) - .to_str() - .expect("CString should not fail here.") - .to_owned(); - - let signature = (*wallet).wallet.sign_message(secret, nonce, &message); + let commitments = match commitments.as_ref() { + None => { + error!(target: LOG_TARGET, "failed to obtain commitments as reference"); + ptr::replace( + error_ptr, + LibWalletError::from(InterfaceError::NullError("commitments vector".to_string())).code as c_int, + ); + return 0; + }, + Some(cs) => match cs.to_commitment_vec() { + Ok(cs) => cs, + Err(e) => { + error!(target: LOG_TARGET, "failed to convert from tari vector: {:?}", e); + ptr::replace(error_ptr, LibWalletError::from(e).code as c_int); + return 0; + }, + }, + }; - match signature { - Ok(s) => { - let hex_sig = s.get_signature().to_hex(); - let hex_nonce = s.get_public_nonce().to_hex(); - let hex_return = format!("{}|{}", hex_sig, hex_nonce); - result = CString::new(hex_return).expect("CString should not fail here."); + match (*wallet).runtime.block_on((*wallet).wallet.coin_split_even( + commitments, + number_of_splits, + MicroTari(fee_per_gram), + String::new(), + )) { + Ok(tx_id) => { + ptr::replace(error_ptr, 0); + tx_id.as_u64() }, Err(e) => { - error = LibWalletError::from(e).code; - ptr::swap(error_out, &mut error as *mut c_int); + error!(target: LOG_TARGET, "failed to join outputs: {:#?}", e); + ptr::replace(error_ptr, LibWalletError::from(e).code); + 0 }, } - - result.into_raw() } -/// Verifies the signature of the message signed by a TariWallet +/// This function will tell the wallet to do a coin join, resulting in a new coin worth a sum of the joined coins minus +/// the fee. /// /// ## Arguments -/// `wallet` - The TariWallet pointer. -/// `public_key` - The pointer to the TariPublicKey of the wallet which originally signed the message -/// `hex_sig_nonce` - The pointer to the sting containing the hexadecimal representation of the -/// signature and public nonce seperated by a pipe character. -/// `msg` - The pointer to the msg the signature will be checked against. -/// `error_out` - Pointer to an int which will be modified to an error code should one occur, may not be null. Functions -/// as an out parameter. -/// ## Returns +/// * `wallet` - The TariWallet pointer +/// * `commitments` - A `TariVector` of "strings", tagged as `TariTypeTag::String`, containing commitment's hex values +/// (see `Commitment::to_hex()`) +/// * `fee_per_gram` - The transaction fee +/// * `error_out` - Pointer to an int which will be modified to an error code should one occur, may not be null. +/// Functions as an out parameter. +/// +/// ## Returns +/// `TariVector` - Returns the transaction id. +/// +/// # Safety +/// `TariVector` must be freed after use with `destroy_tari_vector()` +#[no_mangle] +pub unsafe extern "C" fn wallet_coin_join( + wallet: *mut TariWallet, + commitments: *mut TariVector, + fee_per_gram: u64, + error_ptr: *mut i32, +) -> u64 { + if wallet.is_null() { + error!(target: LOG_TARGET, "wallet pointer is null"); + ptr::replace( + error_ptr, + LibWalletError::from(InterfaceError::NullError("wallet".to_string())).code as c_int, + ); + return 0; + } + + let commitments = match commitments.as_ref() { + None => { + error!(target: LOG_TARGET, "failed to obtain commitments as reference"); + ptr::replace( + error_ptr, + LibWalletError::from(InterfaceError::NullError("commitments vector".to_string())).code as c_int, + ); + return 0; + }, + Some(cs) => match cs.to_commitment_vec() { + Ok(cs) => cs, + Err(e) => { + error!(target: LOG_TARGET, "failed to convert from tari vector: {:?}", e); + ptr::replace(error_ptr, LibWalletError::from(e).code as c_int); + return 0; + }, + }, + }; + + match (*wallet) + .runtime + .block_on((*wallet).wallet.coin_join(commitments, fee_per_gram.into(), None)) + { + Ok(tx_id) => { + ptr::replace(error_ptr, 0); + tx_id.as_u64() + }, + + Err(e) => { + error!(target: LOG_TARGET, "failed to join outputs: {:#?}", e); + ptr::replace(error_ptr, LibWalletError::from(e).code); + 0 + }, + } +} + +/// This function will tell what the outcome of a coin join would be. +/// +/// ## Arguments +/// * `wallet` - The TariWallet pointer +/// * `commitments` - A `TariVector` of "strings", tagged as `TariTypeTag::String`, containing commitment's hex values +/// (see `Commitment::to_hex()`) +/// * `fee_per_gram` - The transaction fee +/// * `error_out` - Pointer to an int which will be modified to an error code should one occur, may not be null. +/// Functions as an out parameter. +/// +/// ## Returns +/// `*mut TariCoinPreview` - A struct with expected output values and the fee. +/// +/// # Safety +/// `TariVector` must be freed after use with `destroy_tari_vector()` +#[no_mangle] +pub unsafe extern "C" fn wallet_preview_coin_join( + wallet: *mut TariWallet, + commitments: *mut TariVector, + fee_per_gram: u64, + error_ptr: *mut i32, +) -> *mut TariCoinPreview { + if wallet.is_null() { + error!(target: LOG_TARGET, "wallet pointer is null"); + ptr::replace( + error_ptr, + LibWalletError::from(InterfaceError::NullError("wallet".to_string())).code as c_int, + ); + return ptr::null_mut(); + } + + let commitments = match commitments.as_ref() { + None => { + error!(target: LOG_TARGET, "failed to obtain commitments as reference"); + ptr::replace( + error_ptr, + LibWalletError::from(InterfaceError::NullError("commitments vector".to_string())).code as c_int, + ); + return ptr::null_mut(); + }, + Some(cs) => match cs.to_commitment_vec() { + Ok(cs) => cs, + Err(e) => { + error!(target: LOG_TARGET, "failed to convert from tari vector: {:?}", e); + ptr::replace(error_ptr, LibWalletError::from(e).code as c_int); + return ptr::null_mut(); + }, + }, + }; + + match (*wallet).runtime.block_on( + (*wallet) + .wallet + .preview_coin_join_with_commitments(commitments, MicroTari(fee_per_gram)), + ) { + Ok((expected_outputs, fee)) => { + ptr::replace(error_ptr, 0); + let mut expected_outputs = ManuallyDrop::new(expected_outputs); + + Box::into_raw(Box::new(TariCoinPreview { + expected_outputs: Box::into_raw(Box::new(TariVector { + tag: TariTypeTag::U64, + len: expected_outputs.len(), + cap: expected_outputs.capacity(), + ptr: expected_outputs.as_mut_ptr() as *mut c_void, + })), + fee: fee.as_u64(), + })) + }, + Err(e) => { + error!( + target: LOG_TARGET, + "failed to preview coin join with commitments: {:#?}", e + ); + ptr::replace(error_ptr, LibWalletError::from(e).code); + ptr::null_mut() + }, + } +} + +/// This function will tell what the outcome of a coin split would be. +/// +/// ## Arguments +/// * `wallet` - The TariWallet pointer +/// * `commitments` - A `TariVector` of "strings", tagged as `TariTypeTag::String`, containing commitment's hex values +/// (see `Commitment::to_hex()`) +/// * `number_of_splits` - The number of times to split the amount +/// * `fee_per_gram` - The transaction fee +/// * `error_out` - Pointer to an int which will be modified to an error code should one occur, may not be null. +/// Functions as an out parameter. +/// +/// ## Returns +/// `*mut TariCoinPreview` - A struct with expected output values and the fee. +/// +/// # Safety +/// `TariVector` must be freed after use with `destroy_tari_vector()` +#[no_mangle] +pub unsafe extern "C" fn wallet_preview_coin_split( + wallet: *mut TariWallet, + commitments: *mut TariVector, + number_of_splits: usize, + fee_per_gram: u64, + error_ptr: *mut i32, +) -> *mut TariCoinPreview { + if wallet.is_null() { + error!(target: LOG_TARGET, "wallet pointer is null"); + ptr::replace( + error_ptr, + LibWalletError::from(InterfaceError::NullError("wallet".to_string())).code as c_int, + ); + return ptr::null_mut(); + } + + let commitments = match commitments.as_ref() { + None => { + error!(target: LOG_TARGET, "failed to obtain commitments as reference"); + ptr::replace( + error_ptr, + LibWalletError::from(InterfaceError::NullError("commitments vector".to_string())).code as c_int, + ); + return ptr::null_mut(); + }, + Some(cs) => match cs.to_commitment_vec() { + Ok(cs) => cs, + Err(e) => { + error!(target: LOG_TARGET, "failed to convert from tari vector: {:?}", e); + ptr::replace(error_ptr, LibWalletError::from(e).code as c_int); + return ptr::null_mut(); + }, + }, + }; + + match (*wallet) + .runtime + .block_on((*wallet).wallet.preview_coin_split_with_commitments_no_amount( + commitments, + number_of_splits, + MicroTari(fee_per_gram), + )) { + Ok((expected_outputs, fee)) => { + ptr::replace(error_ptr, 0); + let mut expected_outputs = ManuallyDrop::new(expected_outputs); + + Box::into_raw(Box::new(TariCoinPreview { + expected_outputs: Box::into_raw(Box::new(TariVector { + tag: TariTypeTag::U64, + len: expected_outputs.len(), + cap: expected_outputs.capacity(), + ptr: expected_outputs.as_mut_ptr() as *mut c_void, + })), + fee: fee.as_u64(), + })) + }, + Err(e) => { + error!( + target: LOG_TARGET, + "failed to preview split with commitments outputs (no amount): {:#?}", e + ); + ptr::replace(error_ptr, LibWalletError::from(e).code); + ptr::null_mut() + }, + } +} + +/// Signs a message using the public key of the TariWallet +/// +/// ## Arguments +/// `wallet` - The TariWallet pointer. +/// `msg` - The message pointer. +/// `error_out` - Pointer to an int which will be modified to an error code should one occur, may not be null. Functions +/// as an out parameter. +/// ## Returns +/// `*mut c_char` - Returns the pointer to the hexadecimal representation of the signature and +/// public nonce, seperated by a pipe character. Empty if an error occured. +/// +/// # Safety +/// The ```string_destroy``` method must be called when finished with a string coming from rust to prevent a memory leak +#[no_mangle] +pub unsafe extern "C" fn wallet_sign_message( + wallet: *mut TariWallet, + msg: *const c_char, + error_out: *mut c_int, +) -> *mut c_char { + let mut error = 0; + let mut result = CString::new("").expect("Blank CString will not fail."); + + ptr::swap(error_out, &mut error as *mut c_int); + if wallet.is_null() { + error = LibWalletError::from(InterfaceError::NullError("wallet".to_string())).code; + ptr::swap(error_out, &mut error as *mut c_int); + return result.into_raw(); + } + + if msg.is_null() { + error = LibWalletError::from(InterfaceError::NullError("message".to_string())).code; + ptr::swap(error_out, &mut error as *mut c_int); + return result.into_raw(); + } + + let nonce = TariPrivateKey::random(&mut OsRng); + let secret = (*wallet).wallet.comms.node_identity().secret_key().clone(); + let message = CStr::from_ptr(msg) + .to_str() + .expect("CString should not fail here.") + .to_owned(); + + let signature = (*wallet).wallet.sign_message(secret, nonce, &message); + + match signature { + Ok(s) => { + let hex_sig = s.get_signature().to_hex(); + let hex_nonce = s.get_public_nonce().to_hex(); + let hex_return = format!("{}|{}", hex_sig, hex_nonce); + result = CString::new(hex_return).expect("CString should not fail here."); + }, + Err(e) => { + error = LibWalletError::from(e).code; + ptr::swap(error_out, &mut error as *mut c_int); + }, + } + + result.into_raw() +} + +/// Verifies the signature of the message signed by a TariWallet +/// +/// ## Arguments +/// `wallet` - The TariWallet pointer. +/// `public_key` - The pointer to the TariPublicKey of the wallet which originally signed the message +/// `hex_sig_nonce` - The pointer to the sting containing the hexadecimal representation of the +/// signature and public nonce seperated by a pipe character. +/// `msg` - The pointer to the msg the signature will be checked against. +/// `error_out` - Pointer to an int which will be modified to an error code should one occur, may not be null. Functions +/// as an out parameter. +/// ## Returns /// `bool` - Returns if the signature is valid or not, will be false if an error occurs. /// /// # Safety @@ -6005,71 +6668,6 @@ pub unsafe extern "C" fn wallet_restart_transaction_broadcast(wallet: *mut TariW } } -/// This function will tell the wallet to do a coin split. -/// -/// ## Arguments -/// `wallet` - The TariWallet pointer -/// `amount` - The amount to split -/// `count` - The number of times to split the amount -/// `fee` - The transaction fee -/// `msg` - Message for split -/// `lock_height` - The number of bocks to lock the transaction for -/// `error_out` - Pointer to an int which will be modified to an error code should one occur, may not be null. Functions -/// as an out parameter. -/// -/// ## Returns -/// `c_ulonglong` - Returns the transaction id. -/// -/// # Safety -/// None -#[no_mangle] -pub unsafe extern "C" fn wallet_coin_split( - wallet: *mut TariWallet, - amount: c_ulonglong, - count: c_ulonglong, - fee: c_ulonglong, - msg: *const c_char, - lock_height: c_ulonglong, - error_out: *mut c_int, -) -> c_ulonglong { - let mut error = 0; - ptr::swap(error_out, &mut error as *mut c_int); - if wallet.is_null() { - error = LibWalletError::from(InterfaceError::NullError("wallet".to_string())).code; - ptr::swap(error_out, &mut error as *mut c_int); - } - - let message; - - if msg.is_null() { - message = "Coin Split".to_string() - } else { - match CStr::from_ptr(msg).to_str() { - Ok(v) => { - message = v.to_owned(); - }, - _ => { - message = "Coin Split".to_string(); - }, - } - }; - - match (*wallet).runtime.block_on((*wallet).wallet.coin_split( - MicroTari(amount), - count as usize, - MicroTari(fee), - message, - Some(lock_height), - )) { - Ok(request_key) => request_key.as_u64(), - Err(e) => { - error = LibWalletError::from(e).code; - ptr::swap(error_out, &mut error as *mut c_int); - 0 - }, - } -} - /// Gets the seed words representing the seed private key of the provided `TariWallet`. /// /// ## Arguments @@ -8895,8 +9493,16 @@ mod test { }); // ascending order - let outputs = wallet_get_utxos(alice_wallet, 1, 20, TariUtxoSort::ValueAsc, 3000, error_ptr); - let utxos: &[TariUtxo] = slice::from_raw_parts_mut((*outputs).ptr, (*outputs).len); + let outputs = wallet_get_utxos( + alice_wallet, + 0, + 20, + TariUtxoSort::ValueAsc, + ptr::null_mut(), + 3000, + error_ptr, + ); + let utxos: &[TariUtxo] = slice::from_raw_parts_mut((*outputs).ptr as *mut TariUtxo, (*outputs).len); assert_eq!(error, 0); assert_eq!((*outputs).len, 6); assert_eq!(utxos.len(), 6); @@ -8907,11 +9513,19 @@ mod test { .fold((true, utxos[0].value), |acc, x| { (acc.0 && x.value > acc.1, x.value) }) .0 ); - destroy_tari_outputs(outputs); + destroy_tari_vector(outputs); // descending order - let outputs = wallet_get_utxos(alice_wallet, 1, 20, TariUtxoSort::ValueDesc, 3000, error_ptr); - let utxos: &[TariUtxo] = slice::from_raw_parts_mut((*outputs).ptr, (*outputs).len); + let outputs = wallet_get_utxos( + alice_wallet, + 0, + 20, + TariUtxoSort::ValueDesc, + ptr::null_mut(), + 3000, + error_ptr, + ); + let utxos: &[TariUtxo] = slice::from_raw_parts_mut((*outputs).ptr as *mut TariUtxo, (*outputs).len); assert_eq!(error, 0); assert_eq!((*outputs).len, 6); assert_eq!(utxos.len(), 6); @@ -8922,15 +9536,23 @@ mod test { .fold((true, utxos[0].value), |acc, x| (acc.0 && x.value < acc.1, x.value)) .0 ); - destroy_tari_outputs(outputs); + destroy_tari_vector(outputs); // result must be empty due to high dust threshold - let outputs = wallet_get_utxos(alice_wallet, 1, 20, TariUtxoSort::ValueAsc, 15000, error_ptr); - let utxos: &[TariUtxo] = slice::from_raw_parts_mut((*outputs).ptr, (*outputs).len); + let outputs = wallet_get_utxos( + alice_wallet, + 0, + 20, + TariUtxoSort::ValueAsc, + ptr::null_mut(), + 15000, + error_ptr, + ); + let utxos: &[TariUtxo] = slice::from_raw_parts_mut((*outputs).ptr as *mut TariUtxo, (*outputs).len); assert_eq!(error, 0); assert_eq!((*outputs).len, 0); assert_eq!(utxos.len(), 0); - destroy_tari_outputs(outputs); + destroy_tari_vector(outputs); string_destroy(network_str as *mut c_char); string_destroy(db_name_alice_str as *mut c_char); @@ -8942,4 +9564,547 @@ mod test { wallet_destroy(alice_wallet); } } + + #[test] + #[allow(clippy::too_many_lines)] + fn test_wallet_get_all_utxos() { + unsafe { + let mut error = 0; + let error_ptr = &mut error as *mut c_int; + let mut recovery_in_progress = true; + let recovery_in_progress_ptr = &mut recovery_in_progress as *mut bool; + + let secret_key_alice = private_key_generate(); + let db_name_alice = CString::new(random::string(8).as_str()).unwrap(); + let db_name_alice_str: *const c_char = CString::into_raw(db_name_alice) as *const c_char; + let alice_temp_dir = tempdir().unwrap(); + let db_path_alice = CString::new(alice_temp_dir.path().to_str().unwrap()).unwrap(); + let db_path_alice_str: *const c_char = CString::into_raw(db_path_alice) as *const c_char; + let transport_config_alice = transport_memory_create(); + let address_alice = transport_memory_get_address(transport_config_alice, error_ptr); + let address_alice_str = CStr::from_ptr(address_alice).to_str().unwrap().to_owned(); + let address_alice_str: *const c_char = CString::new(address_alice_str).unwrap().into_raw() as *const c_char; + let network = CString::new(NETWORK_STRING).unwrap(); + let network_str: *const c_char = CString::into_raw(network) as *const c_char; + + let alice_config = comms_config_create( + address_alice_str, + transport_config_alice, + db_name_alice_str, + db_path_alice_str, + 20, + 10800, + error_ptr, + ); + + let alice_wallet = wallet_create( + alice_config, + ptr::null(), + 0, + 0, + ptr::null(), + ptr::null(), + network_str, + received_tx_callback, + received_tx_reply_callback, + received_tx_finalized_callback, + broadcast_callback, + mined_callback, + mined_unconfirmed_callback, + scanned_callback, + scanned_unconfirmed_callback, + transaction_send_result_callback, + tx_cancellation_callback, + txo_validation_complete_callback, + contacts_liveness_data_updated_callback, + balance_updated_callback, + transaction_validation_complete_callback, + saf_messages_received_callback, + connectivity_status_callback, + recovery_in_progress_ptr, + error_ptr, + ); + + (0..10).for_each(|i| { + let (_, uout) = create_test_input((1000 * i).into(), 0, &PedersenCommitmentFactory::default()); + (*alice_wallet) + .runtime + .block_on((*alice_wallet).wallet.output_manager_service.add_output(uout, None)) + .unwrap(); + }); + + let outputs = wallet_get_utxos( + alice_wallet, + 0, + 100, + TariUtxoSort::ValueAsc, + ptr::null_mut(), + 0, + error_ptr, + ); + let utxos: &[TariUtxo] = slice::from_raw_parts_mut((*outputs).ptr as *mut TariUtxo, (*outputs).len); + assert_eq!(error, 0); + + let payload = utxos[0..3] + .iter() + .map(|x| CStr::from_ptr(x.commitment).to_str().unwrap().to_owned()) + .collect::>(); + + let commitments = Box::into_raw(Box::new(TariVector::from(payload))) as *mut TariVector; + let result = wallet_coin_join(alice_wallet, commitments, 5, error_ptr); + assert_eq!(error, 0); + assert!(result > 0); + + let outputs = wallet_get_all_utxos(alice_wallet, error_ptr); + let utxos: &[TariUtxo] = slice::from_raw_parts_mut((*outputs).ptr as *mut TariUtxo, (*outputs).len); + assert_eq!(error, 0); + assert_eq!((*outputs).len, 11); + assert_eq!(utxos.len(), 11); + destroy_tari_vector(outputs); + + string_destroy(network_str as *mut c_char); + string_destroy(db_name_alice_str as *mut c_char); + string_destroy(db_path_alice_str as *mut c_char); + string_destroy(address_alice_str as *mut c_char); + private_key_destroy(secret_key_alice); + transport_config_destroy(transport_config_alice); + comms_config_destroy(alice_config); + wallet_destroy(alice_wallet); + } + } + + #[test] + #[allow(clippy::too_many_lines, clippy::needless_collect)] + fn test_wallet_coin_join() { + unsafe { + let mut error = 0; + let error_ptr = &mut error as *mut c_int; + let mut recovery_in_progress = true; + let recovery_in_progress_ptr = &mut recovery_in_progress as *mut bool; + + let secret_key_alice = private_key_generate(); + let db_name_alice = CString::new(random::string(8).as_str()).unwrap(); + let db_name_alice_str: *const c_char = CString::into_raw(db_name_alice) as *const c_char; + let alice_temp_dir = tempdir().unwrap(); + let db_path_alice = CString::new(alice_temp_dir.path().to_str().unwrap()).unwrap(); + let db_path_alice_str: *const c_char = CString::into_raw(db_path_alice) as *const c_char; + let transport_config_alice = transport_memory_create(); + let address_alice = transport_memory_get_address(transport_config_alice, error_ptr); + let address_alice_str = CStr::from_ptr(address_alice).to_str().unwrap().to_owned(); + let address_alice_str: *const c_char = CString::new(address_alice_str).unwrap().into_raw() as *const c_char; + let network = CString::new(NETWORK_STRING).unwrap(); + let network_str: *const c_char = CString::into_raw(network) as *const c_char; + + let alice_config = comms_config_create( + address_alice_str, + transport_config_alice, + db_name_alice_str, + db_path_alice_str, + 20, + 10800, + error_ptr, + ); + + let alice_wallet = wallet_create( + alice_config, + ptr::null(), + 0, + 0, + ptr::null(), + ptr::null(), + network_str, + received_tx_callback, + received_tx_reply_callback, + received_tx_finalized_callback, + broadcast_callback, + mined_callback, + mined_unconfirmed_callback, + scanned_callback, + scanned_unconfirmed_callback, + transaction_send_result_callback, + tx_cancellation_callback, + txo_validation_complete_callback, + contacts_liveness_data_updated_callback, + balance_updated_callback, + transaction_validation_complete_callback, + saf_messages_received_callback, + connectivity_status_callback, + recovery_in_progress_ptr, + error_ptr, + ); + + (1..=5).for_each(|i| { + (*alice_wallet) + .runtime + .block_on((*alice_wallet).wallet.output_manager_service.add_output( + create_test_input((15000 * i).into(), 0, &PedersenCommitmentFactory::default()).1, + None, + )) + .unwrap(); + }); + + // ---------------------------------------------------------------------------- + // preview + + let outputs = wallet_get_utxos( + alice_wallet, + 0, + 100, + TariUtxoSort::ValueAsc, + ptr::null_mut(), + 0, + error_ptr, + ); + let utxos: &[TariUtxo] = slice::from_raw_parts_mut((*outputs).ptr as *mut TariUtxo, (*outputs).len); + assert_eq!(error, 0); + + let pre_join_total_amount = utxos[0..3].iter().fold(0u64, |acc, x| acc + x.value); + + let payload = utxos[0..3] + .iter() + .map(|x| CStr::from_ptr(x.commitment).to_str().unwrap().to_owned()) + .collect::>(); + + let commitments = Box::into_raw(Box::new(TariVector::from(payload))) as *mut TariVector; + let preview = wallet_preview_coin_join(alice_wallet, commitments, 5, error_ptr); + assert_eq!(error, 0); + + // ---------------------------------------------------------------------------- + // join + + let outputs = wallet_get_utxos( + alice_wallet, + 0, + 100, + TariUtxoSort::ValueAsc, + ptr::null_mut(), + 0, + error_ptr, + ); + let utxos: &[TariUtxo] = slice::from_raw_parts_mut((*outputs).ptr as *mut TariUtxo, (*outputs).len); + assert_eq!(error, 0); + + let payload = utxos[0..3] + .iter() + .map(|x| CStr::from_ptr(x.commitment).to_str().unwrap().to_owned()) + .collect::>(); + + let commitments = Box::into_raw(Box::new(TariVector::from(payload))) as *mut TariVector; + let result = wallet_coin_join(alice_wallet, commitments, 5, error_ptr); + assert_eq!(error, 0); + assert!(result > 0); + + let unspent_outputs = (*alice_wallet) + .wallet + .output_db + .fetch_outputs_by(OutputBackendQuery { + status: vec![OutputStatus::Unspent], + ..Default::default() + }) + .unwrap() + .into_iter() + .map(|x| x.unblinded_output.value) + .collect::>(); + + let new_pending_outputs = (*alice_wallet) + .wallet + .output_db + .fetch_outputs_by(OutputBackendQuery { + status: vec![OutputStatus::EncumberedToBeReceived], + ..Default::default() + }) + .unwrap() + .into_iter() + .map(|x| x.unblinded_output.value) + .collect::>(); + + let post_join_total_amount = new_pending_outputs.iter().fold(0u64, |acc, x| acc + x.as_u64()); + let expected_output_values: Vec = Vec::from_raw_parts( + (*(*preview).expected_outputs).ptr as *mut u64, + (*(*preview).expected_outputs).len, + (*(*preview).expected_outputs).cap, + ); + + let outputs = wallet_get_utxos( + alice_wallet, + 0, + 20, + TariUtxoSort::ValueAsc, + Box::into_raw(Box::new(TariVector::from(vec![OutputStatus::Unspent]))), + 0, + error_ptr, + ); + let utxos: &[TariUtxo] = slice::from_raw_parts_mut((*outputs).ptr as *mut TariUtxo, (*outputs).len); + assert_eq!(error, 0); + assert_eq!(utxos.len(), 2); + assert_eq!(unspent_outputs.len(), 2); + + // lengths + assert_eq!(new_pending_outputs.len(), 1); + assert_eq!(new_pending_outputs.len(), expected_output_values.len()); + + // comparing result with expected + assert_eq!(new_pending_outputs[0].as_u64(), expected_output_values[0]); + + // checking fee + assert_eq!(pre_join_total_amount - post_join_total_amount, (*preview).fee); + + destroy_tari_vector(outputs); + destroy_tari_vector(commitments); + destroy_tari_coin_preview(preview); + + string_destroy(network_str as *mut c_char); + string_destroy(db_name_alice_str as *mut c_char); + string_destroy(db_path_alice_str as *mut c_char); + string_destroy(address_alice_str as *mut c_char); + private_key_destroy(secret_key_alice); + transport_config_destroy(transport_config_alice); + comms_config_destroy(alice_config); + wallet_destroy(alice_wallet); + } + } + + #[test] + #[allow(clippy::too_many_lines, clippy::needless_collect)] + fn test_wallet_coin_split() { + unsafe { + let mut error = 0; + let error_ptr = &mut error as *mut c_int; + let mut recovery_in_progress = true; + let recovery_in_progress_ptr = &mut recovery_in_progress as *mut bool; + + let secret_key_alice = private_key_generate(); + let db_name_alice = CString::new(random::string(8).as_str()).unwrap(); + let db_name_alice_str: *const c_char = CString::into_raw(db_name_alice) as *const c_char; + let alice_temp_dir = tempdir().unwrap(); + let db_path_alice = CString::new(alice_temp_dir.path().to_str().unwrap()).unwrap(); + let db_path_alice_str: *const c_char = CString::into_raw(db_path_alice) as *const c_char; + let transport_config_alice = transport_memory_create(); + let address_alice = transport_memory_get_address(transport_config_alice, error_ptr); + let address_alice_str = CStr::from_ptr(address_alice).to_str().unwrap().to_owned(); + let address_alice_str: *const c_char = CString::new(address_alice_str).unwrap().into_raw() as *const c_char; + let network = CString::new(NETWORK_STRING).unwrap(); + let network_str: *const c_char = CString::into_raw(network) as *const c_char; + + let alice_config = comms_config_create( + address_alice_str, + transport_config_alice, + db_name_alice_str, + db_path_alice_str, + 20, + 10800, + error_ptr, + ); + + let alice_wallet = wallet_create( + alice_config, + ptr::null(), + 0, + 0, + ptr::null(), + ptr::null(), + network_str, + received_tx_callback, + received_tx_reply_callback, + received_tx_finalized_callback, + broadcast_callback, + mined_callback, + mined_unconfirmed_callback, + scanned_callback, + scanned_unconfirmed_callback, + transaction_send_result_callback, + tx_cancellation_callback, + txo_validation_complete_callback, + contacts_liveness_data_updated_callback, + balance_updated_callback, + transaction_validation_complete_callback, + saf_messages_received_callback, + connectivity_status_callback, + recovery_in_progress_ptr, + error_ptr, + ); + + (1..=5).for_each(|i| { + (*alice_wallet) + .runtime + .block_on((*alice_wallet).wallet.output_manager_service.add_output( + create_test_input((15000 * i).into(), 0, &PedersenCommitmentFactory::default()).1, + None, + )) + .unwrap(); + }); + + // ---------------------------------------------------------------------------- + // preview + + let outputs = wallet_get_utxos( + alice_wallet, + 0, + 100, + TariUtxoSort::ValueAsc, + ptr::null_mut(), + 0, + error_ptr, + ); + let utxos: &[TariUtxo] = slice::from_raw_parts_mut((*outputs).ptr as *mut TariUtxo, (*outputs).len); + assert_eq!(error, 0); + + let pre_split_total_amount = utxos[0..3].iter().fold(0u64, |acc, x| acc + x.value); + + let payload = utxos[0..3] + .iter() + .map(|x| CStr::from_ptr(x.commitment).to_str().unwrap().to_owned()) + .collect::>(); + + let commitments = Box::into_raw(Box::new(TariVector::from(payload))) as *mut TariVector; + + let preview = wallet_preview_coin_split(alice_wallet, commitments, 3, 5, error_ptr); + assert_eq!(error, 0); + destroy_tari_vector(commitments); + + // ---------------------------------------------------------------------------- + // split + + let outputs = wallet_get_utxos( + alice_wallet, + 0, + 100, + TariUtxoSort::ValueAsc, + ptr::null_mut(), + 0, + error_ptr, + ); + let utxos: &[TariUtxo] = slice::from_raw_parts_mut((*outputs).ptr as *mut TariUtxo, (*outputs).len); + assert_eq!(error, 0); + + let payload = utxos[0..3] + .iter() + .map(|x| CStr::from_ptr(x.commitment).to_str().unwrap().to_owned()) + .collect::>(); + + let commitments = Box::into_raw(Box::new(TariVector::from(payload))) as *mut TariVector; + + let result = wallet_coin_split(alice_wallet, commitments, 3, 5, error_ptr); + assert_eq!(error, 0); + assert!(result > 0); + + let unspent_outputs = (*alice_wallet) + .wallet + .output_db + .fetch_outputs_by(OutputBackendQuery { + status: vec![OutputStatus::Unspent], + ..Default::default() + }) + .unwrap() + .into_iter() + .map(|x| x.unblinded_output.value) + .collect::>(); + + let new_pending_outputs = (*alice_wallet) + .wallet + .output_db + .fetch_outputs_by(OutputBackendQuery { + status: vec![OutputStatus::EncumberedToBeReceived], + ..Default::default() + }) + .unwrap() + .into_iter() + .map(|x| x.unblinded_output.value) + .collect::>(); + + let post_split_total_amount = new_pending_outputs.iter().fold(0u64, |acc, x| acc + x.as_u64()); + let expected_output_values: Vec = Vec::from_raw_parts( + (*(*preview).expected_outputs).ptr as *mut u64, + (*(*preview).expected_outputs).len, + (*(*preview).expected_outputs).cap, + ); + + let outputs = wallet_get_utxos( + alice_wallet, + 0, + 20, + TariUtxoSort::ValueAsc, + Box::into_raw(Box::new(TariVector::from(vec![OutputStatus::Unspent]))), + 0, + error_ptr, + ); + let utxos: &[TariUtxo] = slice::from_raw_parts_mut((*outputs).ptr as *mut TariUtxo, (*outputs).len); + assert_eq!(error, 0); + assert_eq!(utxos.len(), 2); + assert_eq!(unspent_outputs.len(), 2); + + // lengths + assert_eq!(new_pending_outputs.len(), 3); + assert_eq!(new_pending_outputs.len(), expected_output_values.len()); + + // comparing resulting output values relative to itself + assert_eq!(new_pending_outputs[0], new_pending_outputs[1]); + assert_eq!(new_pending_outputs[2], new_pending_outputs[1] + MicroTari(1)); + + // comparing resulting output values to the expected + assert_eq!(new_pending_outputs[0].as_u64(), expected_output_values[0]); + assert_eq!(new_pending_outputs[1].as_u64(), expected_output_values[1]); + assert_eq!(new_pending_outputs[2].as_u64(), expected_output_values[2]); + + // checking fee + assert_eq!(pre_split_total_amount - post_split_total_amount, (*preview).fee); + + destroy_tari_vector(outputs); + destroy_tari_vector(commitments); + destroy_tari_coin_preview(preview); + + string_destroy(network_str as *mut c_char); + string_destroy(db_name_alice_str as *mut c_char); + string_destroy(db_path_alice_str as *mut c_char); + string_destroy(address_alice_str as *mut c_char); + private_key_destroy(secret_key_alice); + transport_config_destroy(transport_config_alice); + comms_config_destroy(alice_config); + wallet_destroy(alice_wallet); + } + } + + #[test] + fn test_tari_vector() { + let mut error = 0; + + unsafe { + let tv = create_tari_vector(TariTypeTag::Text); + assert_eq!((*tv).tag, TariTypeTag::Text); + assert_eq!((*tv).len, 0); + assert_eq!((*tv).cap, 2); + + tari_vector_push_string( + tv, + CString::new("test string 1").unwrap().into_raw() as *const c_char, + &mut error as *mut c_int, + ); + assert_eq!(error, 0); + assert_eq!((*tv).tag, TariTypeTag::Text); + assert_eq!((*tv).len, 1); + assert_eq!((*tv).cap, 1); + + tari_vector_push_string( + tv, + CString::new("test string 2").unwrap().into_raw() as *const c_char, + &mut error as *mut c_int, + ); + assert_eq!(error, 0); + assert_eq!((*tv).tag, TariTypeTag::Text); + assert_eq!((*tv).len, 2); + assert_eq!((*tv).cap, 2); + + tari_vector_push_string( + tv, + CString::new("test string 3").unwrap().into_raw() as *const c_char, + &mut error as *mut c_int, + ); + assert_eq!(error, 0); + assert_eq!((*tv).tag, TariTypeTag::Text); + assert_eq!((*tv).len, 3); + assert_eq!((*tv).cap, 3); + + destroy_tari_vector(tv); + } + } } diff --git a/base_layer/wallet_ffi/tari_wallet_ffi.h b/base_layer/wallet_ffi/wallet.h similarity index 94% rename from base_layer/wallet_ffi/tari_wallet_ffi.h rename to base_layer/wallet_ffi/wallet.h index ecc08623b4..9648644b34 100644 --- a/base_layer/wallet_ffi/tari_wallet_ffi.h +++ b/base_layer/wallet_ffi/wallet.h @@ -1,16 +1,31 @@ // Copyright 2022. The Tari Project // SPDX-License-Identifier: BSD-3-Clause +// This file was generated by cargo-bindgen. Please do not edit manually. + #include #include #include #include +/** + * The number of unique fields available. This always matches the number of variants in `OutputField`. + */ +#define OutputFields_NUM_FIELDS 10 + +enum TariTypeTag { + Text = 0, + Utxo = 1, + Commitment = 2, + U64 = 3, + I64 = 4, +}; + enum TariUtxoSort { - ValueAsc, - ValueDesc, - MinedHeightAsc, - MinedHeightDesc, + ValueAsc = 0, + ValueDesc = 1, + MinedHeightAsc = 2, + MinedHeightDesc = 3, }; /** @@ -132,6 +147,21 @@ struct TransactionSendStatus; struct TransportConfig; +/** + * -------------------------------- Vector ------------------------------------------------ /// + */ +struct TariVector { + enum TariTypeTag tag; + uintptr_t len; + uintptr_t cap; + void *ptr; +}; + +struct TariCoinPreview { + struct TariVector *expected_outputs; + uint64_t fee; +}; + typedef TransactionKernel TariTransactionKernel; /** @@ -260,20 +290,77 @@ typedef struct P2pConfig TariCommsConfig; typedef struct Balance TariBalance; +typedef struct FeePerGramStatsResponse TariFeePerGramStats; + +typedef struct FeePerGramStat TariFeePerGramStat; + struct TariUtxo { - char *commitment; + const char *commitment; uint64_t value; + uint64_t mined_height; + uint64_t mined_timestamp; + uint8_t status; }; -struct TariOutputs { - uintptr_t len; - uintptr_t cap; - struct TariUtxo *ptr; -}; +#ifdef __cplusplus +extern "C" { +#endif // __cplusplus -typedef struct FeePerGramStatsResponse TariFeePerGramStats; +/** + * Initialize a new `TariVector` + * + * ## Arguments + * `tag` - A predefined type-tag of the vector's payload. + * + * ## Returns + * `*mut TariVector` - Returns a pointer to a `TariVector`. + * + * # Safety + * `destroy_tari_vector()` must be called to free the allocated memory. + */ +struct TariVector *create_tari_vector(enum TariTypeTag tag); + +/** + * Appending a given value to the back of the vector. + * + * ## Arguments + * `s` - An item to push. + * + * ## Returns + * + * + * # Safety + * `destroy_tari_vector()` must be called to free the allocated memory. + */ +void tari_vector_push_string(struct TariVector *tv, const char *s, int32_t *error_ptr); -typedef FeePerGramStat TariFeePerGramStat; +/** + * Frees memory allocated for `TariVector`. + * + * ## Arguments + * `v` - The pointer to `TariVector` + * + * ## Returns + * `()` - Does not return a value, equivalent to void in C + * + * # Safety + * None + */ +void destroy_tari_vector(struct TariVector *v); + +/** + * Frees memory allocated for `TariCoinPreview`. + * + * ## Arguments + * `v` - The pointer to `TariCoinPreview` + * + * ## Returns + * `()` - Does not return a value, equivalent to void in C + * + * # Safety + * None + */ +void destroy_tari_coin_preview(struct TariCoinPreview *p); /** * -------------------------------- Strings ------------------------------------------------ /// @@ -2199,44 +2286,158 @@ TariBalance *wallet_get_balance(struct TariWallet *wallet, * This function returns a list of unspent UTXO values and commitments. * * ## Arguments - * `wallet` - The TariWallet pointer, - * `page` - Page offset, - * `page_size` - A number of items per page, - * `sorting` - An enum representing desired sorting, - * `dust_threshold` - A value filtering threshold. Outputs whose values are <= `dust_threshold` are not listed in the + * * `wallet` - The TariWallet pointer, + * * `page` - Page offset, + * * `page_size` - A number of items per page, + * * `sorting` - An enum representing desired sorting, + * * `dust_threshold` - A value filtering threshold. Outputs whose values are <= `dust_threshold` are not listed in the * result. - * `error_out` - A pointer to an int which will be modified to an error - * code should one occur, may not be null. Functions as an out parameter. + * * `error_out` - Pointer to an int which will be modified to an error code should one occur, may not be null. + * Functions as an out parameter. * * ## Returns - * `*mut TariOutputs` - Returns a struct with an array pointer, length and capacity (needed for proper destruction + * `*mut TariVector` - Returns a struct with an array pointer, length and capacity (needed for proper destruction * after use). * * # Safety - * `destroy_tari_outputs()` must be called after use. + * `destroy_tari_vector()` must be called after use. + * Items that fail to produce `.as_transaction_output()` are omitted from the list and a `warn!()` message is logged to + * LOG_TARGET. + */ +struct TariVector *wallet_get_utxos(struct TariWallet *wallet, + uintptr_t page, + uintptr_t page_size, + enum TariUtxoSort sorting, + struct TariVector *states, + uint64_t dust_threshold, + int32_t *error_ptr); + +/** + * This function returns a list of all UTXO values, commitment's hex values and states. + * + * ## Arguments + * * `wallet` - The TariWallet pointer, + * * `error_out` - Pointer to an int which will be modified to an error code should one occur, may not be null. + * Functions as an out parameter. + * + * ## Returns + * `*mut TariVector` - Returns a struct with an array pointer, length and capacity (needed for proper destruction + * after use). + * + * ## States + * 0 - Unspent + * 1 - Spent + * 2 - EncumberedToBeReceived + * 3 - EncumberedToBeSpent + * 4 - Invalid + * 5 - CancelledInbound + * 6 - UnspentMinedUnconfirmed + * 7 - ShortTermEncumberedToBeReceived + * 8 - ShortTermEncumberedToBeSpent + * 9 - SpentMinedUnconfirmed + * 10 - AbandonedCoinbase + * 11 - NotStored + * + * # Safety + * `destroy_tari_vector()` must be called after use. * Items that fail to produce `.as_transaction_output()` are omitted from the list and a `warn!()` message is logged to * LOG_TARGET. */ -struct TariOutputs *wallet_get_utxos(struct TariWallet *wallet, - uintptr_t page, - uintptr_t page_size, - enum TariUtxoSort sorting, - uint64_t dust_threshold, - int32_t *error_out); +struct TariVector *wallet_get_all_utxos(struct TariWallet *wallet, + int32_t *error_ptr); /** - * Frees memory for a `TariOutputs` + * This function will tell the wallet to do a coin split. * * ## Arguments - * `x` - The pointer to `TariOutputs` + * * `wallet` - The TariWallet pointer + * * `commitments` - A `TariVector` of "strings", tagged as `TariTypeTag::String`, containing commitment's hex values + * (see `Commitment::to_hex()`) + * * `number_of_splits` - The number of times to split the amount + * * `fee_per_gram` - The transaction fee + * * `error_out` - Pointer to an int which will be modified to an error code should one occur, may not be null. + * Functions as an out parameter. * * ## Returns - * `()` - Does not return a value, equivalent to void in C + * `c_ulonglong` - Returns the transaction id. * * # Safety - * None + * `TariVector` must be freed after use with `destroy_tari_vector()` + */ +uint64_t wallet_coin_split(struct TariWallet *wallet, + struct TariVector *commitments, + uintptr_t number_of_splits, + uint64_t fee_per_gram, + int32_t *error_ptr); + +/** + * This function will tell the wallet to do a coin join, resulting in a new coin worth a sum of the joined coins minus + * the fee. + * + * ## Arguments + * * `wallet` - The TariWallet pointer + * * `commitments` - A `TariVector` of "strings", tagged as `TariTypeTag::String`, containing commitment's hex values + * (see `Commitment::to_hex()`) + * * `fee_per_gram` - The transaction fee + * * `error_out` - Pointer to an int which will be modified to an error code should one occur, may not be null. + * Functions as an out parameter. + * + * ## Returns + * `TariVector` - Returns the transaction id. + * + * # Safety + * `TariVector` must be freed after use with `destroy_tari_vector()` + */ +uint64_t wallet_coin_join(struct TariWallet *wallet, + struct TariVector *commitments, + uint64_t fee_per_gram, + int32_t *error_ptr); + +/** + * This function will tell what the outcome of a coin join would be. + * + * ## Arguments + * * `wallet` - The TariWallet pointer + * * `commitments` - A `TariVector` of "strings", tagged as `TariTypeTag::String`, containing commitment's hex values + * (see `Commitment::to_hex()`) + * * `fee_per_gram` - The transaction fee + * * `error_out` - Pointer to an int which will be modified to an error code should one occur, may not be null. + * Functions as an out parameter. + * + * ## Returns + * `*mut TariCoinPreview` - A struct with expected output values and the fee. + * + * # Safety + * `TariVector` must be freed after use with `destroy_tari_vector()` + */ +struct TariCoinPreview *wallet_preview_coin_join(struct TariWallet *wallet, + struct TariVector *commitments, + uint64_t fee_per_gram, + int32_t *error_ptr); + +/** + * This function will tell what the outcome of a coin split would be. + * + * ## Arguments + * * `wallet` - The TariWallet pointer + * * `commitments` - A `TariVector` of "strings", tagged as `TariTypeTag::String`, containing commitment's hex values + * (see `Commitment::to_hex()`) + * * `number_of_splits` - The number of times to split the amount + * * `fee_per_gram` - The transaction fee + * * `error_out` - Pointer to an int which will be modified to an error code should one occur, may not be null. + * Functions as an out parameter. + * + * ## Returns + * `*mut TariCoinPreview` - A struct with expected output values and the fee. + * + * # Safety + * `TariVector` must be freed after use with `destroy_tari_vector()` */ -void destroy_tari_outputs(struct TariOutputs *x); +struct TariCoinPreview *wallet_preview_coin_split(struct TariWallet *wallet, + struct TariVector *commitments, + uintptr_t number_of_splits, + uint64_t fee_per_gram, + int32_t *error_ptr); /** * Signs a message using the public key of the TariWallet @@ -2824,33 +3025,6 @@ unsigned long long wallet_start_transaction_validation(struct TariWallet *wallet bool wallet_restart_transaction_broadcast(struct TariWallet *wallet, int *error_out); -/** - * This function will tell the wallet to do a coin split. - * - * ## Arguments - * `wallet` - The TariWallet pointer - * `amount` - The amount to split - * `count` - The number of times to split the amount - * `fee` - The transaction fee - * `msg` - Message for split - * `lock_height` - The number of bocks to lock the transaction for - * `error_out` - Pointer to an int which will be modified to an error code should one occur, may not be null. Functions - * as an out parameter. - * - * ## Returns - * `c_ulonglong` - Returns the transaction id. - * - * # Safety - * None - */ -unsigned long long wallet_coin_split(struct TariWallet *wallet, - unsigned long long amount, - unsigned long long count, - unsigned long long fee, - const char *msg, - unsigned long long lock_height, - int *error_out); - /** * Gets the seed words representing the seed private key of the provided `TariWallet`. * @@ -3359,3 +3533,7 @@ unsigned long long fee_per_gram_stat_get_max_fee_per_gram(TariFeePerGramStat *fe * None */ void fee_per_gram_stat_destroy(TariFeePerGramStat *fee_per_gram_stat); + +#ifdef __cplusplus +} // extern "C" +#endif // __cplusplus diff --git a/buildtools/create_osx_install_zip.sh b/buildtools/create_osx_install_zip.sh index 6778b8e032..fe5f081536 100755 --- a/buildtools/create_osx_install_zip.sh +++ b/buildtools/create_osx_install_zip.sh @@ -76,9 +76,6 @@ cp -f "${project_dir}/target/release/tari_collectibles" "${tarball_folder}/runti # Validator node cp -f "${project_dir}/target/release/tari_validator_node" "${tarball_folder}/runtime/tari_validator_node" -# Launchpad -cp -f "${project_dir}/target/release/tari_launchpad" "${tarball_folder}/runtime/tari_launchpad" - # 3rd party install cp -f "${local_dir}/install_xmrig.sh" "${tarball_folder}/runtime/install_xmrig.sh" cp -f "${local_dir}/get_xmrig_osx.ps1" "${tarball_folder}/runtime/get_xmrig_osx.ps1" diff --git a/buildtools/create_ubuntu_install_zip.sh b/buildtools/create_ubuntu_install_zip.sh index 6d3d192cfa..abc5a390c2 100755 --- a/buildtools/create_ubuntu_install_zip.sh +++ b/buildtools/create_ubuntu_install_zip.sh @@ -74,9 +74,6 @@ cp -f "${project_dir}/target/release/tari_collectibles" "${tarball_folder}/runti # Validator node cp -f "${project_dir}/target/release/tari_validator_node" "${tarball_folder}/runtime/tari_validator_node" -# Launchpad -cp -f "${project_dir}/target/release/tari_launchpad" "${tarball_folder}/runtime/tari_launchpad" - # 3rd party install cp -f "${local_dir}/install_xmrig.sh" "${tarball_folder}/runtime/install_xmrig.sh" cp -f "${local_dir}/get_xmrig_ubuntu.ps1" "${tarball_folder}/runtime/get_xmrig_ubuntu.ps1" diff --git a/changelog.md b/changelog.md index cbd0e4bd33..27e62dfc4a 100644 --- a/changelog.md +++ b/changelog.md @@ -120,6 +120,64 @@ All notable changes to this project will be documented in this file. See [standa * **core:** replace OutputFlags with OutputType ([#4174](https://github.com/tari-project/tari/issues/4174)) ([d779f43](https://github.com/tari-project/tari/commit/d779f4311a0415b3ecd98e806bfbf27fc2486412)) +### [0.32.12](https://github.com/tari-project/tari/compare/v0.32.11...v0.32.12) (2022-07-11) + + +### Bug Fixes + +* cbindgen fix ([#4298](https://github.com/tari-project/tari/issues/4298)) ([2744d46](https://github.com/tari-project/tari/commit/2744d4601f6e3db461515d894314d25364faa59b)) + +### [0.32.11](https://github.com/tari-project/tari/compare/v0.32.10...v0.32.11) (2022-07-11) + + +### Bug Fixes + +* fixed bug in wallet_coin_join ([#4290](https://github.com/tari-project/tari/issues/4290)) ([2f14c3c](https://github.com/tari-project/tari/commit/2f14c3c1e867eff785b5417a72bacedf17df5022)) + +### [0.32.10](https://github.com/tari-project/tari/compare/v0.32.9...v0.32.10) (2022-07-07) + + +### Features + +* add mined_timestamp to wallet.db ([#4267](https://github.com/tari-project/tari/issues/4267)) ([c6c9832](https://github.com/tari-project/tari/commit/c6c9832f7ea72b648f4faeebf246677094033a19)) +* **wallet_ffi:** added mined_timestamp to TariUtxo ([#4284](https://github.com/tari-project/tari/issues/4284)) ([6e1b3da](https://github.com/tari-project/tari/commit/6e1b3da20e1b0e54cf64ea9ee7cb8f930065f7c3)) + + +### Bug Fixes + +* improve GHA docker image builds ([#4257](https://github.com/tari-project/tari/issues/4257)) ([2c01421](https://github.com/tari-project/tari/commit/2c0142121feed0d45ca57a5e19a2fed2aada62bc)) +* removed code duplication in TariUtxo conversion ([#4283](https://github.com/tari-project/tari/issues/4283)) ([455d161](https://github.com/tari-project/tari/commit/455d161e42609a072878b9a99de5890753b828cb)) + +### [0.32.9](https://github.com/tari-project/tari/compare/v0.32.8...v0.32.9) (2022-07-06) + + +### Features + +* **ffi:** added 3 functions ([#4266](https://github.com/tari-project/tari/issues/4266)) ([2be17df](https://github.com/tari-project/tari/commit/2be17dfa337d9175d56e0879b64f0dd4875bbe66)) + +### [0.32.8](https://github.com/tari-project/tari/compare/v0.32.7...v0.32.8) (2022-07-04) + + +### Features + +* **wallet_ffi:** wallet_coin_split changes ([#4254](https://github.com/tari-project/tari/issues/4254)) ([d367f0b](https://github.com/tari-project/tari/commit/d367f0b56fc86df3662f1d01b5037f63163892a3)) + +### [0.32.7](https://github.com/tari-project/tari/compare/v0.32.6...v0.32.7) (2022-06-30) + + +### Features + +* **wallet_ffi:** wallet_get_utxos, wallet_coin_join, wallet_coin_split ([#4244](https://github.com/tari-project/tari/issues/4244)) ([88931aa](https://github.com/tari-project/tari/commit/88931aa5eea3f574fda44b37bc4b973dd5a6e125)) + +### [0.32.6](https://github.com/tari-project/tari/compare/v0.32.5...v0.32.6) (2022-06-29) + + +### Features + +* **ci:** build both x86/arm64 docker images from GHA ([#4204](https://github.com/tari-project/tari/issues/4204)) ([28a8f8b](https://github.com/tari-project/tari/commit/28a8f8b541f96d2bee4bd7f46cc1625dfeb0d323)) +* **wallet_ffi:** add coin join and split ([#4218](https://github.com/tari-project/tari/issues/4218)) ([af6c834](https://github.com/tari-project/tari/commit/af6c834c9e38984135152e664823a02857d09656)) +* **wallet:** allow UTXO selection by specific outputs and by token ([#4227](https://github.com/tari-project/tari/issues/4227)) ([f2a7e18](https://github.com/tari-project/tari/commit/f2a7e1846341a69ddea6eb3541467e82e1bf2e47)) + ### [0.32.5](https://github.com/tari-project/tari/compare/v0.32.4...v0.32.5) (2022-06-20) diff --git a/lints.toml b/lints.toml index 244e61c0c9..b9e39b0144 100644 --- a/lints.toml +++ b/lints.toml @@ -24,7 +24,6 @@ deny = [ 'clippy::create_dir', 'clippy::dbg_macro', 'clippy::else_if_without_else', - 'clippy::enum_glob_use', 'clippy::inline_always', 'clippy::let_underscore_drop', 'clippy::let_unit_value', diff --git a/scripts/file_license_check.sh b/scripts/file_license_check.sh index b65be5509c..d7a9d56f33 100755 --- a/scripts/file_license_check.sh +++ b/scripts/file_license_check.sh @@ -2,7 +2,7 @@ # Must be run from the repo root rg -i "Copyright.*The Tari Project" --files-without-match \ - -g '!*.{Dockerfile,asc,bat,config,config.js,css,csv,drawio,gitkeep,hbs,html,iss,json,lock,md,min.js,ps1,py,rc,scss,sh,sql,svg,toml,txt,yml,vue}' . \ + -g '!*.{Dockerfile,asc,bat,config,config.js,css,csv,drawio,env,gitkeep,hbs,html,ini,iss,json,lock,md,min.js,ps1,py,rc,scss,sh,sql,svg,toml,txt,yml,vue}' . \ | sort > /tmp/rgtemp # sort the .license.ignore file as sorting seems to behave differently on different platforms diff --git a/scripts/update_crate_metadata.sh b/scripts/update_crate_metadata.sh index 43437b082a..cf3a358c52 100755 --- a/scripts/update_crate_metadata.sh +++ b/scripts/update_crate_metadata.sh @@ -47,7 +47,6 @@ function update_versions { comms/core comms/dht comms/rpc_macros - applications/launchpad/backend applications/tari_base_node applications/tari_app_grpc applications/tari_app_utilities